Merge branch 'jk/unused-params-even-more'
authorJunio C Hamano <gitster@pobox.com>
Thu, 25 Apr 2019 07:41:12 +0000 (16:41 +0900)
committerJunio C Hamano <gitster@pobox.com>
Thu, 25 Apr 2019 07:41:12 +0000 (16:41 +0900)
Code cleanup.

* jk/unused-params-even-more:
parse_opt_ref_sorting: always use with NONEG flag
pretty: drop unused strbuf from parse_padding_placeholder()
pretty: drop unused "type" parameter in needs_rfc2047_encoding()
parse-options: drop unused ctx parameter from show_gitcomp()
fetch_pack(): drop unused parameters
report_path_error(): drop unused prefix parameter
unpack-trees: drop unused error_type parameters
unpack-trees: drop name_entry from traverse_by_cache_tree()
test-date: drop unused "now" parameter from parse_dates()
update-index: drop unused prefix_length parameter from do_reupdate()
log: drop unused "len" from show_tagger()
log: drop unused rev_info from early output
revision: drop some unused "revs" parameters

237 files changed:
.github/CONTRIBUTING.md
.github/PULL_REQUEST_TEMPLATE.md
.gitignore
Documentation/.gitignore
Documentation/CodingGuidelines
Documentation/Makefile
Documentation/RelNotes/2.22.0.txt
Documentation/SubmittingPatches
Documentation/asciidoctor-extensions.rb
Documentation/config/branch.txt
Documentation/config/core.txt
Documentation/config/diff.txt
Documentation/config/fsck.txt
Documentation/config/gc.txt
Documentation/config/gpg.txt
Documentation/config/pull.txt
Documentation/config/rebase.txt
Documentation/diff-options.txt
Documentation/doc-diff
Documentation/git-add.txt
Documentation/git-am.txt
Documentation/git-checkout.txt
Documentation/git-cherry-pick.txt
Documentation/git-clean.txt
Documentation/git-config.txt
Documentation/git-daemon.txt
Documentation/git-diff-tree.txt
Documentation/git-filter-branch.txt
Documentation/git-grep.txt
Documentation/git-help.txt
Documentation/git-http-backend.txt
Documentation/git-interpret-trailers.txt
Documentation/git-ls-remote.txt
Documentation/git-ls-tree.txt
Documentation/git-merge.txt
Documentation/git-notes.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-remote-ext.txt
Documentation/git-remote-fd.txt
Documentation/git-remote-helpers.txto
Documentation/git-remote-testgit.txt [deleted file]
Documentation/git-reset.txt
Documentation/git-revert.txt
Documentation/git-stash.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-svn.txt
Documentation/git-web--browse.txt
Documentation/git-worktree.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/gitignore.txt
Documentation/gitk.txt
Documentation/gitmodules.txt
Documentation/gitremote-helpers.txt
Documentation/gitrepository-layout.txt
Documentation/gitweb.conf.txt
Documentation/gitweb.txt
Documentation/glossary-content.txt
Documentation/howto/setup-git-server-over-http.txt
Documentation/rev-list-options.txt
Documentation/revisions.txt
Documentation/sequencer.txt
Documentation/technical/api-config.txt
Documentation/technical/directory-rename-detection.txt
Documentation/technical/pack-protocol.txt
Documentation/technical/protocol-v2.txt
Documentation/urls.txt
Makefile
builtin.h
builtin/checkout.c
builtin/init-db.c
builtin/log.c
builtin/multi-pack-index.c
builtin/rebase--interactive.c
builtin/rebase.c
builtin/reset.c
builtin/stash.c [new file with mode: 0644]
builtin/update-index.c
builtin/worktree.c
cache.h
ci/install-dependencies.sh
ci/test-documentation.sh
config.c
contrib/coccinelle/flex_alloc.cocci [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/subtree/git-subtree.sh
convert.c
diff.c
git-legacy-rebase.sh [deleted file]
git-legacy-stash.sh [new file with mode: 0755]
git-mergetool--lib.sh
git-sh-setup.sh
git-stash.sh [deleted file]
git-submodule.sh
git.c
help.c
http-walker.c
http.c
http.h
ident.c
line-log.c
mergetools/smerge [new file with mode: 0644]
midx.c
packfile.c
packfile.h
parse-options.c
path.c
progress.c
progress.h
protocol.c
read-cache.c
rebase-interactive.c
rebase-interactive.h
refs/files-backend.c
remote-curl.c
revision.c
sequencer.c
sequencer.h
sha1-name.c
sha1collisiondetection
sha1dc/sha1.c
strbuf.c
strbuf.h
submodule.c
t/README
t/interop/i5500-git-daemon.sh
t/lib-git-daemon.sh
t/lib-git-p4.sh
t/lib-git-svn.sh
t/lib-httpd.sh
t/perf/perf-lib.sh
t/t0000-basic.sh
t/t0001-init.sh
t/t0040-parse-options.sh
t/t0301-credential-cache.sh
t/t0410-partial-clone.sh
t/t1305-config-include.sh
t/t1404-update-ref-errors.sh
t/t1415-worktree-refs.sh
t/t2023-checkout-m.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3429-rebase-edit-todo.sh
t/t3430-rebase-merges.sh
t/t3600-rm.sh
t/t3903-stash.sh
t/t3905-stash-include-untracked.sh
t/t3907-stash-show-config.sh [new file with mode: 0755]
t/t4014-format-patch.sh
t/t4038-diff-combined.sh
t/t4150-am.sh
t/t4211-line-log.sh
t/t5317-pack-objects-filter-objects.sh
t/t5318-commit-graph.sh
t/t5400-send-pack.sh
t/t5407-post-rewrite-hook.sh
t/t5500-fetch-pack.sh
t/t5503-tagfollow.sh
t/t5510-fetch.sh
t/t5512-ls-remote.sh
t/t5515-fetch-merge-logic.sh
t/t5516-fetch-push.sh
t/t5531-deep-submodule-push.sh
t/t5537-fetch-shallow.sh
t/t5539-fetch-http-shallow.sh
t/t5540-http-push-webdav.sh
t/t5541-http-push-smart.sh
t/t5542-push-http-shallow.sh
t/t5545-push-options.sh
t/t5550-http-fetch-dumb.sh
t/t5551-http-fetch-smart.sh
t/t5552-skipping-fetch-negotiator.sh
t/t5561-http-backend.sh
t/t5570-git-daemon.sh
t/t5581-http-curl-verbose.sh
t/t5601-clone.sh
t/t5616-partial-clone.sh
t/t5700-protocol-v1.sh
t/t5702-protocol-v2.sh
t/t5703-upload-pack-ref-in-want.sh
t/t5812-proto-disable-http.sh
t/t7113-post-index-change-hook.sh [new file with mode: 0755]
t/t7406-submodule-update.sh
t/t7525-status-rename.sh
t/t7810-grep.sh
t/t9115-git-svn-dcommit-funky-renames.sh
t/t9118-git-svn-funky-branch-names.sh
t/t9120-git-svn-clone-with-percent-escapes.sh
t/t9142-git-svn-shallow-clone.sh
t/t9800-git-p4-basic.sh
t/t9801-git-p4-branch.sh
t/t9802-git-p4-filetype.sh
t/t9803-git-p4-shell-metachars.sh
t/t9804-git-p4-label.sh
t/t9805-git-p4-skip-submit-edit.sh
t/t9806-git-p4-options.sh
t/t9807-git-p4-submit.sh
t/t9808-git-p4-chdir.sh
t/t9809-git-p4-client-view.sh
t/t9810-git-p4-rcs.sh
t/t9811-git-p4-label-import.sh
t/t9812-git-p4-wildcards.sh
t/t9813-git-p4-preserve-users.sh
t/t9814-git-p4-rename.sh
t/t9815-git-p4-submit-fail.sh
t/t9816-git-p4-locked.sh
t/t9817-git-p4-exclude.sh
t/t9818-git-p4-block.sh
t/t9819-git-p4-case-folding.sh
t/t9820-git-p4-editor-handling.sh
t/t9821-git-p4-path-variations.sh
t/t9822-git-p4-path-encoding.sh
t/t9823-git-p4-mock-lfs.sh
t/t9824-git-p4-git-lfs.sh
t/t9825-git-p4-handle-utf16-without-bom.sh
t/t9826-git-p4-keep-empty-commits.sh
t/t9827-git-p4-change-filetype.sh
t/t9828-git-p4-map-user.sh
t/t9829-git-p4-jobs.sh
t/t9830-git-p4-symlink-dir.sh
t/t9831-git-p4-triggers.sh
t/t9832-unshelve.sh
t/t9833-errors.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
trace2.c
trace2.h
trace2/tr2_tgt_event.c
trace2/tr2_tgt_normal.c
trace2/tr2_tgt_perf.c
unicode-width.h
unpack-trees.c
index 64e605a02b71c51e9f59c429b28961c3152039b9..e7b4e2f3c204c2c94c60222abbc702bd7d72de39 100644 (file)
@@ -5,7 +5,7 @@ Git community does not use github.com for their contributions. Instead, we use
 a mailing list (git@vger.kernel.org) for code submissions, code
 reviews, and bug reports.
 
-Nevertheless, you can use [submitGit](http://submitgit.herokuapp.com/) to
+Nevertheless, you can use [GitGitGadget](https://gitgitgadget.github.io/) to
 conveniently send your Pull Requests commits to our mailing list.
 
 Please read ["A note from the maintainer"](https://git.kernel.org/pub/scm/git/git.git/plain/MaintNotes?h=todo)
index adba13e5baf4603de72341068532e2c7d7d05f75..952c7c3a2aa11ea1087390be61eab6f7c0013599 100644 (file)
@@ -1,7 +1,7 @@
 Thanks for taking the time to contribute to Git! Please be advised that the
 Git community does not use github.com for their contributions. Instead, we use
 a mailing list (git@vger.kernel.org) for code submissions, code reviews, and
-bug reports. Nevertheless, you can use submitGit to conveniently send your Pull
-Requests commits to our mailing list.
+bug reports. Nevertheless, you can use GitGitGadget (https://gitgitgadget.github.io/)
+to conveniently send your Pull Requests commits to our mailing list.
 
 Please read the "guidelines for contributing" linked above!
index 7374587f9df901cba0a6990ad48fac17f8a5be57..44c74402c8c6dedfa24a9f5da6c596c7153b9d25 100644 (file)
@@ -82,7 +82,7 @@
 /git-init-db
 /git-interpret-trailers
 /git-instaweb
-/git-legacy-rebase
+/git-legacy-stash
 /git-log
 /git-ls-files
 /git-ls-remote
index 3ef54e0adbad5fbfd4468581037a2dcb9a9a4681..bf2bf271b5678895e94810dcbdca12b0dfb7de91 100644 (file)
@@ -13,3 +13,4 @@ mergetools-*.txt
 manpage-base-url.xsl
 SubmittingPatches.txt
 tmp-doc-diff/
+GIT-ASCIIDOCFLAGS
index 8579530710a7e2b665941be45264429b99d5134b..32210a4386c32972b7c4dbfa36b0cf977dd85de2 100644 (file)
@@ -580,11 +580,14 @@ Writing Documentation:
  or commands:
 
  Literal examples (e.g. use of command-line options, command names,
- branch names, configuration and environment variables) must be
- typeset in monospace (i.e. wrapped with backticks):
+ branch names, URLs, pathnames (files and directories), configuration and
+ environment variables) must be typeset in monospace (i.e. wrapped with
+ backticks):
    `--pretty=oneline`
    `git rev-list`
    `remote.pushDefault`
+   `http://git.example.com`
+   `.git/config`
    `GIT_DIR`
    `HEAD`
 
index 26a2342beaf0132241980dbbcaf8592fd4d183dc..6d738f831ebaf316ac79b6a3eb9c544cd18ea7c5 100644 (file)
@@ -12,7 +12,6 @@ MAN1_TXT += $(filter-out \
                $(wildcard git-*.txt))
 MAN1_TXT += git.txt
 MAN1_TXT += gitk.txt
-MAN1_TXT += gitremote-helpers.txt
 MAN1_TXT += gitweb.txt
 
 MAN5_TXT += gitattributes.txt
@@ -30,6 +29,7 @@ MAN7_TXT += gitdiffcore.txt
 MAN7_TXT += giteveryday.txt
 MAN7_TXT += gitglossary.txt
 MAN7_TXT += gitnamespaces.txt
+MAN7_TXT += gitremote-helpers.txt
 MAN7_TXT += gitrevisions.txt
 MAN7_TXT += gitsubmodules.txt
 MAN7_TXT += gittutorial-2.txt
@@ -331,6 +331,15 @@ mergetools-list.made: ../git-mergetool--lib.sh $(wildcard ../mergetools/*)
                show_tool_names can_merge "* " || :' >mergetools-merge.txt && \
        date >$@
 
+TRACK_ASCIIDOCFLAGS = $(subst ','\'',$(ASCIIDOC_COMMON):$(ASCIIDOC_HTML):$(ASCIIDOC_DOCBOOK))
+
+GIT-ASCIIDOCFLAGS: FORCE
+       @FLAGS='$(TRACK_ASCIIDOCFLAGS)'; \
+           if test x"$$FLAGS" != x"`cat GIT-ASCIIDOCFLAGS 2>/dev/null`" ; then \
+               echo >&2 "    * new asciidoc flags"; \
+               echo "$$FLAGS" >GIT-ASCIIDOCFLAGS; \
+            fi
+
 clean:
        $(RM) *.xml *.xml+ *.html *.html+ *.1 *.5 *.7
        $(RM) *.texi *.texi+ *.texi++ git.info gitman.info
@@ -340,13 +349,14 @@ clean:
        $(RM) SubmittingPatches.txt
        $(RM) $(cmds_txt) $(mergetools_txt) *.made
        $(RM) manpage-base-url.xsl
+       $(RM) GIT-ASCIIDOCFLAGS
 
-$(MAN_HTML): %.html : %.txt asciidoc.conf
+$(MAN_HTML): %.html : %.txt asciidoc.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
        $(TXT_TO_HTML) -d manpage -o $@+ $< && \
        mv $@+ $@
 
-$(OBSOLETE_HTML): %.html : %.txto asciidoc.conf
+$(OBSOLETE_HTML): %.html : %.txto asciidoc.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
        $(TXT_TO_HTML) -o $@+ $< && \
        mv $@+ $@
@@ -354,16 +364,16 @@ $(OBSOLETE_HTML): %.html : %.txto asciidoc.conf
 manpage-base-url.xsl: manpage-base-url.xsl.in
        $(QUIET_GEN)sed "s|@@MAN_BASE_URL@@|$(MAN_BASE_URL)|" $< > $@
 
-%.1 %.5 %.7 : %.xml manpage-base-url.xsl
+%.1 %.5 %.7 : %.xml manpage-base-url.xsl $(wildcard manpage*.xsl)
        $(QUIET_XMLTO)$(RM) $@ && \
        $(XMLTO) -m $(MANPAGE_XSL) $(XMLTO_EXTRA) man $<
 
-%.xml : %.txt asciidoc.conf
+%.xml : %.txt asciidoc.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
        $(TXT_TO_XML) -d manpage -o $@+ $< && \
        mv $@+ $@
 
-user-manual.xml: user-manual.txt user-manual.conf
+user-manual.xml: user-manual.txt user-manual.conf asciidoctor-extensions.rb GIT-ASCIIDOCFLAGS
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
        $(TXT_TO_XML) -d book -o $@+ $< && \
        mv $@+ $@
@@ -373,7 +383,8 @@ technical/api-index.txt: technical/api-index-skel.txt \
        $(QUIET_GEN)cd technical && '$(SHELL_PATH_SQ)' ./api-index.sh
 
 technical/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
-$(patsubst %,%.html,$(API_DOCS) technical/api-index $(TECH_DOCS)): %.html : %.txt asciidoc.conf
+$(patsubst %,%.html,$(API_DOCS) technical/api-index $(TECH_DOCS)): %.html : %.txt \
+       asciidoc.conf GIT-ASCIIDOCFLAGS
        $(QUIET_ASCIIDOC)$(TXT_TO_HTML) $*.txt
 
 SubmittingPatches.txt: SubmittingPatches
@@ -430,7 +441,7 @@ $(patsubst %,%.html,$(ARTICLES)) : %.html : %.txt
 WEBDOC_DEST = /pub/software/scm/git/docs
 
 howto/%.html: ASCIIDOC_EXTRA += -a git-relative-html-prefix=../
-$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt
+$(patsubst %.txt,%.html,$(wildcard howto/*.txt)): %.html : %.txt GIT-ASCIIDOCFLAGS
        $(QUIET_ASCIIDOC)$(RM) $@+ $@ && \
        sed -e '1,/^$$/d' $< | \
        $(TXT_TO_HTML) - >$@+ && \
index ca774e8388d58eb270f5003108cdf110dd145fa7..d7d230e5398b357d0b26aa4c95cc724e2925392c 100644 (file)
@@ -36,6 +36,19 @@ UI, Workflows & Features
    a merge commit.  This has been updated to use a more modern and
    human readable output that still is concise enough.
 
+ * "git rebase --rebase-merges" replaces its old "--preserve-merges"
+   option; the latter is now marked as deprecated.
+
+ * Error message given while cloning with --recurse-submodules has
+   been updated.
+
+ * The completion helper code now pays attention to repository-local
+   configuration (when available), which allows --list-cmds to honour
+   a repository specific setting of completion.commands, for example.
+
+ * "git mergetool" learned to offer Sublime Merge (smerge) as one of
+   its backends.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -55,6 +68,32 @@ Performance, Internal Implementation, Development Support etc.
  * The command line parser of "git commit-tree" has been rewritten to
    use the parse-options API.
 
+ * Suggest GitGitGadget instead of submitGit as a way to submit
+   patches based on GitHub PR to us.
+
+ * The test framework has been updated to help developers by making it
+   easier to run most of the tests under different versions of
+   over-the-wire protocols.
+
+ * Dev support update to make it easier to compare two formatted
+   results from our documentation.
+
+ * The scripted "git rebase" implementation has been retired.
+
+ * "git multi-pack-index verify" did not scale well with the number of
+   packfiles, which is being improved.
+
+ * "git stash" has been rewritten in C.
+
+ * The "check-docs" Makefile target to support developers has been
+   updated.
+
+ * The tests have been updated not to rely on the abbreviated option
+   names the parse-options API offers, to protect us from an
+   abbreviated form of an option that used to be unique within the
+   command getting non-unique when a new option that share the same
+   prefix is added.
+
 
 Fixes since v2.21
 -----------------
@@ -134,6 +173,73 @@ Fixes since v2.21
    now fixed.
    (merge cd8e7593b9 jk/config-type-color-ends-with-lf later to maint).
 
+ * When the "clean" filter can reduce the size of a huge file in the
+   working tree down to a small "token" (a la Git LFS), there is no
+   point in allocating a huge scratch area upfront, but the buffer is
+   sized based on the original file size.  The convert mechanism now
+   allocates very minimum and reallocates as it receives the output
+   from the clean filter process.
+   (merge 02156ab031 jh/resize-convert-scratch-buffer later to maint).
+
+ * "git rebase" uses the refs/rewritten/ hierarchy to store its
+   intermediate states, which inherently makes the hierarchy per
+   worktree, but it didn't quite work well.
+   (merge b9317d55a3 nd/rewritten-ref-is-per-worktree later to maint).
+
+ * "git log -L<from>,<to>:<path>" with "-s" did not suppress the patch
+   output as it should.  This has been corrected.
+   (merge 05314efaea jk/line-log-with-patch later to maint).
+
+ * "git worktree add" used to do a "find an available name with stat
+   and then mkdir", which is race-prone.  This has been fixed by using
+   mkdir and reacting to EEXIST in a loop.
+   (merge 7af01f2367 ms/worktree-add-atomic-mkdir later to maint).
+
+ * Build update for SHA-1 with collision detection.
+   (merge 07a20f569b jk/sha1dc later to maint).
+
+ * Build procedure has been fixed around use of asciidoctor instead of
+   asciidoc.
+   (merge 185f9a0ea0 ma/asciidoctor-fixes later to maint).
+
+ * remote-http transport did not anonymize URLs reported in its error
+   messages at places.
+   (merge c1284b21f2 js/anonymize-remote-curl-diag later to maint).
+
+ * Error messages given from the http transport have been updated so
+   that they can be localized.
+   (merge ed8b4132c8 js/remote-curl-i18n later to maint).
+
+ * "git init" forgot to read platform-specific repository
+   configuration, which made Windows port to ignore settings of
+   core.hidedotfiles, for example.
+
+ * A corner-case object name ambiguity while the sequencer machinery
+   is working (e.g. "rebase -i -x") has been fixed.
+
+ * "git format-patch" did not diagnose an error while opening the
+   output file for the cover-letter, which has been corrected.
+   (merge 2fe95f494c jc/format-patch-error-check later to maint).
+
+ * "git checkout -f <branch>" while the index has an unmerged path
+   incorrectly left some paths in an unmerged state, which has been
+   corrected.
+
+ * A corner case bug in the refs API has been corrected.
+   (merge d3322eb28b jk/refs-double-abort later to maint).
+
+ * Unicode update.
+   (merge 584b62c37b bb/unicode-12 later to maint).
+
+ * dumb-http walker has been updated to share more error recovery
+   strategy with the normal codepath.
+
+ * A buglet in configuration parser has been fixed.
+   (merge 19e7fdaa58 nd/include-if-wildmatch later to maint).
+
+ * The documentation for "git read-tree --reset -u" has been updated.
+   (merge b5a0bd694c nd/read-tree-reset-doc later to maint).
+
  * Code cleanup, docfix, build fix, etc.
    (merge 11f470aee7 jc/test-yes-doc later to maint).
    (merge 90503a240b js/doc-symref-in-proto-v1 later to maint).
@@ -148,3 +254,16 @@ Fixes since v2.21
    (merge 716a5af812 rd/gc-prune-doc-fix later to maint).
    (merge 50b206371d js/untravis-windows later to maint).
    (merge dbf47215e3 js/rebase-recreate-merge later to maint).
+   (merge 56cb2d30f8 dl/reset-doc-no-wrt-abbrev later to maint).
+   (merge 64eca306a2 ja/dir-rename-doc-markup-fix later to maint).
+   (merge af91b0230c dl/ignore-docs later to maint).
+   (merge 59a06e947b ra/t3600-test-path-funcs later to maint).
+   (merge e041d0781b ar/t4150-remove-cruft later to maint).
+   (merge 8d75a1d183 ma/asciidoctor-fixes-more later to maint).
+   (merge 74cc547b0f mh/pack-protocol-doc-fix later to maint).
+   (merge ed31851fa6 ab/doc-misc-typofixes later to maint).
+   (merge a7256debd4 nd/checkout-m-doc-update later to maint).
+   (merge 3a9e1ad78d jt/t5551-protocol-v2-does-not-have-half-auth later to maint).
+   (merge 0b918b75af sg/t5318-cleanup later to maint).
+   (merge 68ed71b53c cb/doco-mono later to maint).
+   (merge a34dca2451 nd/interpret-trailers-docfix later to maint).
index ec8b2051450abb90a77b1edf243d9dd01b16867e..6d589e118c17f7e9807e4b7534de00879a75306b 100644 (file)
@@ -372,15 +372,15 @@ such as "Thanks-to:", "Based-on-patch-by:", or "Mentored-by:".
 Some parts of the system have dedicated maintainers with their own
 repositories.
 
-- 'git-gui/' comes from git-gui project, maintained by Pat Thoyts:
+- `git-gui/` comes from git-gui project, maintained by Pat Thoyts:
 
        git://repo.or.cz/git-gui.git
 
-- 'gitk-git/' comes from Paul Mackerras's gitk project:
+- `gitk-git/` comes from Paul Mackerras's gitk project:
 
        git://ozlabs.org/~paulus/gitk
 
-- 'po/' comes from the localization coordinator, Jiang Xin:
+- `po/` comes from the localization coordinator, Jiang Xin:
 
        https://github.com/git-l10n/git-po/
 
index ec83b4959eb4d8b278ca6e769cbbaca0cba7cb06..0089e0cfb80df9a283b220ba4187e38f5612557d 100644 (file)
@@ -11,12 +11,12 @@ module Git
       def process(parent, target, attrs)
         if parent.document.basebackend? 'html'
           prefix = parent.document.attr('git-relative-html-prefix')
-          %(<a href="#{prefix}#{target}.html">#{target}(#{attrs[1]})</a>\n)
+          %(<a href="#{prefix}#{target}.html">#{target}(#{attrs[1]})</a>)
         elsif parent.document.basebackend? 'docbook'
           "<citerefentry>\n" \
             "<refentrytitle>#{target}</refentrytitle>" \
             "<manvolnum>#{attrs[1]}</manvolnum>\n" \
-          "</citerefentry>\n"
+          "</citerefentry>"
         end
       end
     end
index 019d60ede2873ffb76e96e75ab0502c8840f75b6..8f4b3faadd47b6c655f403528615ae4f0e0df9f6 100644 (file)
@@ -85,9 +85,9 @@ When `merges`, pass the `--rebase-merges` option to 'git rebase'
 so that the local merge commits are included in the rebase (see
 linkgit:git-rebase[1] for details).
 +
-When preserve, also pass `--preserve-merges` along to 'git rebase'
-so that locally committed merge commits will not be flattened
-by running 'git pull'.
+When `preserve` (deprecated in favor of `merges`), also pass
+`--preserve-merges` along to 'git rebase' so that locally committed merge
+commits will not be flattened by running 'git pull'.
 +
 When the value is `interactive`, the rebase is run in interactive mode.
 +
index 7e9b6c8f4c2d36116be6699c0dc822222393dae6..75538d27e7e06b2041f556dc5e3fcd4aef1abf20 100644 (file)
@@ -414,7 +414,7 @@ Common unit suffixes of 'k', 'm', or 'g' are supported.
 core.excludesFile::
        Specifies the pathname to the file that contains patterns to
        describe paths that are not meant to be tracked, in addition
-       to '.gitignore' (per-directory) and '.git/info/exclude'.
+       to `.gitignore` (per-directory) and `.git/info/exclude`.
        Defaults to `$XDG_CONFIG_HOME/git/ignore`.
        If `$XDG_CONFIG_HOME` is either not set or empty, `$HOME/.config/git/ignore`
        is used instead. See linkgit:gitignore[5].
@@ -429,8 +429,8 @@ core.askPass::
        command-line argument and write the password on its STDOUT.
 
 core.attributesFile::
-       In addition to '.gitattributes' (per-directory) and
-       '.git/info/attributes', Git looks into this file for attributes
+       In addition to `.gitattributes` (per-directory) and
+       `.git/info/attributes`, Git looks into this file for attributes
        (see linkgit:gitattributes[5]). Path expansions are made the same
        way as for `core.excludesFile`. Its default value is
        `$XDG_CONFIG_HOME/git/attributes`. If `$XDG_CONFIG_HOME` is either not
@@ -438,10 +438,10 @@ core.attributesFile::
 
 core.hooksPath::
        By default Git will look for your hooks in the
-       '$GIT_DIR/hooks' directory. Set this to different path,
-       e.g. '/etc/git/hooks', and Git will try to find your hooks in
-       that directory, e.g. '/etc/git/hooks/pre-receive' instead of
-       in '$GIT_DIR/hooks/pre-receive'.
+       `$GIT_DIR/hooks` directory. Set this to different path,
+       e.g. `/etc/git/hooks`, and Git will try to find your hooks in
+       that directory, e.g. `/etc/git/hooks/pre-receive` instead of
+       in `$GIT_DIR/hooks/pre-receive`.
 +
 The path can be either absolute or relative. A relative path is
 taken as relative to the directory where the hooks are run (see
index e48bb987d789e033e1da012a7f950177848b5b23..2c4c9ba27aa48a2eea4951c8a4cffc4f718a75ce 100644 (file)
@@ -10,7 +10,7 @@ diff.autoRefreshIndex::
 
 diff.dirstat::
        A comma separated list of `--dirstat` parameters specifying the
-       default behavior of the `--dirstat` option to linkgit:git-diff[1]`
+       default behavior of the `--dirstat` option to linkgit:git-diff[1]
        and friends. The defaults can be overridden on the command line
        (using `--dirstat=<param1,param2,...>`). The fallback defaults
        (when not changed by `diff.dirstat`) are `changes,noncumulative,3`.
@@ -73,7 +73,7 @@ diff.external::
        environment variable.  The command is called with parameters
        as described under "git Diffs" in linkgit:git[1].  Note: if
        you want to use an external diff program only on a subset of
-       your files, you might want to use linkgit:gitattributes[5] instead.
+       your files, you might want to use linkgit:gitattributes[5] instead.
 
 diff.ignoreSubmodules::
        Sets the default value of --ignore-submodules. Note that this
index 879c5a29c47ddfee5ef3d0ad96895a787d23d285..450e8c38e34338170a806f9e43a99ac37b2e8f2f 100644 (file)
@@ -23,9 +23,9 @@ When `fsck.<msg-id>` is set, errors can be switched to warnings and
 vice versa by configuring the `fsck.<msg-id>` setting where the
 `<msg-id>` is the fsck message ID and the value is one of `error`,
 `warn` or `ignore`. For convenience, fsck prefixes the error/warning
-with the message ID, e.g. "missingEmail: invalid author/committer line
-- missing email" means that setting `fsck.missingEmail = ignore` will
-hide that issue.
+with the message ID, e.g. "missingEmail: invalid author/committer
+line - missing email" means that setting `fsck.missingEmail = ignore`
+will hide that issue.
 +
 In general, it is better to enumerate existing objects with problems
 with `fsck.skipList`, instead of listing the kind of breakages these
index c6fbb8a96f9ee59dfe93a61e4f7a9e3f95f2d7d9..73c08b0c00cb124b3de230f770a1c48cc27e4a21 100644 (file)
@@ -19,7 +19,7 @@ gc.autoPackLimit::
        When there are more than this many packs that are not
        marked with `*.keep` file in the repository, `git gc
        --auto` consolidates them into one larger pack.  The
-       default value is 50.  Setting this to 0 disables it.
+       default value is 50.  Setting this to 0 disables it.
 
 gc.autoDetach::
        Make `git gc --auto` return immediately and run in background
index 590fe0d4baa1740bc1f2abc38ce73340adf59ed6..f999f8ea491d0238abaca2037535c0374f28bc01 100644 (file)
@@ -16,5 +16,5 @@ gpg.format::
 gpg.<format>.program::
        Use this to customize the program used for the signing format you
        chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
-       be used as a legacy synonym for `gpg.openpgp.program`. The default
+       be used as a legacy synonym for `gpg.openpgp.program`. The default
        value for `gpg.x509.program` is "gpgsm".
index bb23a9947d78dcbf528b27b0ebb11d3e6e01811a..b87cab31b39c4469fc2528c04bd19b6b96912dd7 100644 (file)
@@ -18,9 +18,9 @@ When `merges`, pass the `--rebase-merges` option to 'git rebase'
 so that the local merge commits are included in the rebase (see
 linkgit:git-rebase[1] for details).
 +
-When preserve, also pass `--preserve-merges` along to 'git rebase'
-so that locally committed merge commits will not be flattened
-by running 'git pull'.
+When `preserve` (deprecated in favor of `merges`), also pass
+`--preserve-merges` along to 'git rebase' so that locally committed merge
+commits will not be flattened by running 'git pull'.
 +
 When the value is `interactive`, the rebase is run in interactive mode.
 +
index 331d250e0468df8ec51bdb3348d6a30274da26ed..d98e32d812e76f24f6eca49e32af910b1ddd0967 100644 (file)
@@ -1,16 +1,9 @@
 rebase.useBuiltin::
-       Set to `false` to use the legacy shellscript implementation of
-       linkgit:git-rebase[1]. Is `true` by default, which means use
-       the built-in rewrite of it in C.
-+
-The C rewrite is first included with Git version 2.20. This option
-serves an an escape hatch to re-enable the legacy version in case any
-bugs are found in the rewrite. This option and the shellscript version
-of linkgit:git-rebase[1] will be removed in some future release.
-+
-If you find some reason to set this option to `false` other than
-one-off testing you should report the behavior difference as a bug in
-git.
+       Unused configuration variable. Used in Git versions 2.20 and
+       2.21 as an escape hatch to enable the legacy shellscript
+       implementation of rebase. Now the built-in rewrite of it in C
+       is always used. Setting this will emit a warning, to alert any
+       remaining users that setting this now does nothing.
 
 rebase.stat::
        Whether to show a diffstat of what changed upstream since the last
index 5ebc56867bf8323c2715389875c2ea732b5c2ba8..09faee3b44db2e78ba909d30c62ff105255c7832 100644 (file)
@@ -436,7 +436,7 @@ endif::git-format-patch[]
 
 --binary::
        In addition to `--full-index`, output a binary diff that
-       can be applied with `git-apply`.
+       can be applied with `git-apply`. Implies `--patch`.
 
 --abbrev[=<n>]::
        Instead of showing the full 40-byte hexadecimal object
index 32c83dd26f4adf87fd9d6a3ebc6400f6bc108fc7..3355be479812375d8a01d316d26425c3d14be63d 100755 (executable)
@@ -12,9 +12,16 @@ OPTIONS_SPEC="\
 doc-diff [options] <from> <to> [-- <diff-options>]
 doc-diff (-c|--clean)
 --
-j=n    parallel argument to pass to make
-f      force rebuild; do not rely on cached results
-c,clean        cleanup temporary working files
+j=n                    parallel argument to pass to make
+f                      force rebuild; do not rely on cached results
+c,clean                        cleanup temporary working files
+from-asciidoc          use asciidoc with the 'from'-commit
+from-asciidoctor       use asciidoctor with the 'from'-commit
+asciidoc               use asciidoc with both commits
+to-asciidoc            use asciidoc with the 'to'-commit
+to-asciidoctor         use asciidoctor with the 'to'-commit
+asciidoctor            use asciidoctor with both commits
+cut-header-footer      cut away header and footer
 "
 SUBDIRECTORY_OK=1
 . "$(git --exec-path)/git-sh-setup"
@@ -22,6 +29,9 @@ SUBDIRECTORY_OK=1
 parallel=
 force=
 clean=
+from_program=
+to_program=
+cut_header_footer=
 while test $# -gt 0
 do
        case "$1" in
@@ -31,6 +41,22 @@ do
                clean=t ;;
        -f)
                force=t ;;
+       --from-asciidoctor)
+               from_program=-asciidoctor ;;
+       --to-asciidoctor)
+               to_program=-asciidoctor ;;
+       --asciidoctor)
+               from_program=-asciidoctor
+               to_program=-asciidoctor ;;
+       --from-asciidoc)
+               from_program=-asciidoc ;;
+       --to-asciidoc)
+               to_program=-asciidoc ;;
+       --asciidoc)
+               from_program=-asciidoc
+               to_program=-asciidoc ;;
+       --cut-header-footer)
+               cut_header_footer=-cut-header-footer ;;
        --)
                shift; break ;;
        *)
@@ -79,6 +105,22 @@ then
        ln -s "$dots/config.mak" "$tmp/worktree/config.mak"
 fi
 
+construct_makemanflags () {
+       if test "$1" = "-asciidoc"
+       then
+               echo USE_ASCIIDOCTOR=
+       elif test "$1" = "-asciidoctor"
+       then
+               echo USE_ASCIIDOCTOR=YesPlease
+       fi
+}
+
+from_makemanflags=$(construct_makemanflags "$from_program") &&
+to_makemanflags=$(construct_makemanflags "$to_program") &&
+
+from_dir=$from_oid$from_program$cut_header_footer &&
+to_dir=$to_oid$to_program$cut_header_footer &&
+
 # generate_render_makefile <srcdir> <dstdir>
 generate_render_makefile () {
        find "$1" -type f |
@@ -94,7 +136,7 @@ generate_render_makefile () {
        done
 }
 
-# render_tree <committish_oid>
+# render_tree <committish_oid> <directory_name> <makemanflags>
 render_tree () {
        # Skip install-man entirely if we already have an installed directory.
        # We can't rely on make here, since "install-man" unconditionally
@@ -102,28 +144,44 @@ render_tree () {
        # we then can't rely on during the render step). We use "mv" to make
        # sure we don't get confused by a previous run that failed partway
        # through.
-       if ! test -d "$tmp/installed/$1"
+       oid=$1 &&
+       dname=$2 &&
+       makemanflags=$3 &&
+       if ! test -d "$tmp/installed/$dname"
        then
-               git -C "$tmp/worktree" checkout --detach "$1" &&
+               git -C "$tmp/worktree" checkout --detach "$oid" &&
                make -j$parallel -C "$tmp/worktree" \
+                       $makemanflags \
                        GIT_VERSION=omitted \
                        SOURCE_DATE_EPOCH=0 \
-                       DESTDIR="$tmp/installed/$1+" \
+                       DESTDIR="$tmp/installed/$dname+" \
                        install-man &&
-               mv "$tmp/installed/$1+" "$tmp/installed/$1"
+               mv "$tmp/installed/$dname+" "$tmp/installed/$dname"
        fi &&
 
        # As with "installed" above, we skip the render if it's already been
        # done.  So using make here is primarily just about running in
        # parallel.
-       if ! test -d "$tmp/rendered/$1"
+       if ! test -d "$tmp/rendered/$dname"
        then
-               generate_render_makefile "$tmp/installed/$1" "$tmp/rendered/$1+" |
+               generate_render_makefile "$tmp/installed/$dname" \
+                       "$tmp/rendered/$dname+" |
                make -j$parallel -f - &&
-               mv "$tmp/rendered/$1+" "$tmp/rendered/$1"
+               mv "$tmp/rendered/$dname+" "$tmp/rendered/$dname"
+
+               if test "$cut_header_footer" = "-cut-header-footer"
+               then
+                       for f in $(find "$tmp/rendered/$dname" -type f)
+                       do
+                               tail -n +3 "$f" | head -n -2 |
+                               sed -e '1{/^$/d}' -e '${/^$/d}' >"$f+" &&
+                               mv "$f+" "$f" ||
+                               return 1
+                       done
+               fi
        fi
 }
 
-render_tree $from_oid &&
-render_tree $to_oid &&
-git -C $tmp/rendered diff --no-index "$@" $from_oid $to_oid
+render_tree $from_oid $from_dir $from_makemanflags &&
+render_tree $to_oid $to_dir $to_makemanflags &&
+git -C $tmp/rendered diff --no-index "$@" $from_dir $to_dir
index 37bcab94d5e435849c22ddd0473be5cfb1c56140..8b0e4c7fa8c5922801164a7f847a7baecc456402 100644 (file)
@@ -193,15 +193,6 @@ for "git add --no-all <pathspec>...", i.e. ignored removed files.
        for command-line options).
 
 
-CONFIGURATION
--------------
-
-The optional configuration variable `core.excludesFile` indicates a path to a
-file containing patterns of file names to exclude from git-add, similar to
-$GIT_DIR/info/exclude.  Patterns in the exclude file are used in addition to
-those in info/exclude.  See linkgit:gitignore[5].
-
-
 EXAMPLES
 --------
 
index 6f6c34b0f4bc9ba18ec890dff1a6fe10af2fd68f..fc3b993c3338b5f9f8a034aaade14e77bdcd75b5 100644 (file)
@@ -99,6 +99,11 @@ default.   You can use `--no-utf8` to override this.
        am.threeWay configuration variable. For more information,
        see am.threeWay in linkgit:git-config[1].
 
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+       Allow the rerere mechanism to update the index with the
+       result of auto-conflict resolution if possible.
+
 --ignore-space-change::
 --ignore-whitespace::
 --whitespace=<option>::
index f179b43732aa2a7e211a95889847294890c90d38..877e5f503a66145912886c14917f0e68512e6284 100644 (file)
@@ -242,6 +242,8 @@ should result in deletion of the path).
 +
 When checking out paths from the index, this option lets you recreate
 the conflicted merge in the specified paths.
++
+When switching branches with `--merge`, staged changes may be lost.
 
 --conflict=<style>::
        The same as --merge option above, but changes the way the
index b8cfeec67e5fbdcfe1705a3b249f97b284932e79..d64e72462fed7c7c1cd7f0c5543bcd035ce70247 100644 (file)
@@ -148,6 +148,11 @@ effect to your index in a row.
        Pass the merge strategy-specific option through to the
        merge strategy.  See linkgit:git-merge[1] for details.
 
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+       Allow the rerere mechanism to update the index with the
+       result of auto-conflict resolution if possible.
+
 SEQUENCER SUBCOMMANDS
 ---------------------
 include::sequencer.txt[]
index 03056dad0de5ab6121c1f7a506dc4dacf1f83c7c..db876f7dde9237c47d1083d1649f8f8b077b9c82 100644 (file)
@@ -55,14 +55,13 @@ OPTIONS
 
 -e <pattern>::
 --exclude=<pattern>::
-       In addition to those found in .gitignore (per directory) and
-       $GIT_DIR/info/exclude, also consider these patterns to be in the
-       set of the ignore rules in effect.
+       Use the given exclude pattern in addition to the standard ignore rules
+       (see linkgit:gitignore[5]).
 
 -x::
-       Don't use the standard ignore rules read from .gitignore (per
-       directory) and $GIT_DIR/info/exclude, but do still use the ignore
-       rules given with `-e` options.  This allows removing all untracked
+       Don't use the standard ignore rules (see linkgit:gitignore[5]), but
+       still use the ignore rules given with `-e` options from the command
+       line.  This allows removing all untracked
        files, including build products.  This can be used (possibly in
        conjunction with 'git reset') to create a pristine
        working directory to test a clean build.
index d0b9c50d20489e421139b531b66d0b0e693c9781..ff9310f9588a4a13028d1d4182a056262e1079d4 100644 (file)
@@ -126,7 +126,7 @@ See also <<FILES>>.
 
 --local::
        For writing options: write to the repository `.git/config` file.
-       This is the default behavior.
+       This is the default behavior.
 +
 For reading options: read only from the repository `.git/config` rather than
 from all available files.
index 56d54a489875652e754f7cd16ee5a77c2f5e5202..fdc28c041c7286bdbd87bc80c9a2b04521c9d0e2 100644 (file)
@@ -57,7 +57,7 @@ OPTIONS
        This is sort of "Git root" - if you run 'git daemon' with
        '--base-path=/srv/git' on example.com, then if you later try to pull
        'git://example.com/hello.git', 'git daemon' will interpret the path
-       as '/srv/git/hello.git'.
+       as `/srv/git/hello.git`.
 
 --base-path-relaxed::
        If --base-path is enabled and repo lookup fails, with this option
index 24f32e8c544a455829b25193ac6b646504ab5f34..5c8a2a5e9755db17be4063cdcb2edb8b48237436 100644 (file)
@@ -118,6 +118,7 @@ include::pretty-options.txt[]
 
 
 include::pretty-formats.txt[]
+
 include::diff-format.txt[]
 
 GIT
index e6f08ab189489ec1631169d0ad0b190428883235..6b53dd7e06a2cd68f35de19679b4f04c3a189be9 100644 (file)
@@ -189,7 +189,7 @@ to other tags will be rewritten to point to the underlying commit.
        rewriting.  When applying a tree filter, the command needs to
        temporarily check out the tree to some directory, which may consume
        considerable space in case of large projects.  By default it
-       does this in the '.git-rewrite/' directory but you can override
+       does this in the `.git-rewrite/` directory but you can override
        that choice by this parameter.
 
 -f::
index 84fe236a8ee2fd47814c4c7ee854c47279921169..2d27969057fd241922173d286a048e495b74bff0 100644 (file)
@@ -88,7 +88,7 @@ OPTIONS
        mechanism. Only useful with `--untracked`.
 
 --exclude-standard::
-       Do not pay attention to ignored files specified via the `.gitignore`
+       Do not pay attention to ignored files specified via the `.gitignore`
        mechanism.  Only useful when searching files in the current directory
        with `--no-index`.
 
index c318bf87e174f18798a2fcb0530bec05a34c4c92..f71db0daa24358e8589281d06ee48e7cd877bca8 100644 (file)
@@ -171,8 +171,8 @@ variable, we launch 'kfmclient' to try to open the man page on an
 already opened konqueror in a new tab if possible.
 
 For consistency, we also try such a trick if 'man.konqueror.path' is
-set to something like 'A_PATH_TO/konqueror'. That means we will try to
-launch 'A_PATH_TO/kfmclient' instead.
+set to something like `A_PATH_TO/konqueror`. That means we will try to
+launch `A_PATH_TO/kfmclient` instead.
 
 If you really want to use 'konqueror', then you can use something like
 the following:
index bb0db195cebd6b8c3824fde4cc3ccdcec4d96543..558966aa837930538010217752c3d94dee29dccc 100644 (file)
@@ -162,7 +162,7 @@ ScriptAliasMatch ^/git/[^/]*(.*) /usr/libexec/git-core/git-http-backend/storage.
 
 Accelerated static Apache 2.x::
        Similar to the above, but Apache can be used to return static
-       files that are stored on disk.  On many systems this may
+       files that are stored on disk.  On many systems this may
        be more efficient as Apache can ask the kernel to copy the
        file contents from the file system directly to the network:
 +
index a5e8b36f62bcf5eeedfb5a04ac852c476d9f43f3..96ec6499f001e6a5f3cf11de02768033191cb687 100644 (file)
@@ -3,7 +3,7 @@ git-interpret-trailers(1)
 
 NAME
 ----
-git-interpret-trailers - add or parse structured information in commit messages
+git-interpret-trailers - Add or parse structured information in commit messages
 
 SYNOPSIS
 --------
index b9fd3770a6ce19c341c421e07b68985d89d94df5..0b057cbb100c4a3013b3836f40bb065ebee0b8bd 100644 (file)
@@ -31,7 +31,7 @@ OPTIONS
        displayed.
 
 --refs::
-       Do not show peeled tags or pseudorefs like HEAD in the output.
+       Do not show peeled tags or pseudorefs like `HEAD` in the output.
 
 -q::
 --quiet::
index 9dee7bef35fb1df1afa2fcabebe22043c66b6fba..a7515714da1aaa77dddf31272f2fb62a174ba95b 100644 (file)
@@ -27,9 +27,9 @@ in the current working directory.  Note that:
    taken as relative to the current working directory.  E.g. when you are
    in a directory 'sub' that has a directory 'dir', you can run 'git
    ls-tree -r HEAD dir' to list the contents of the tree (that is
-   'sub/dir' in `HEAD`).  You don't want to give a tree that is not at the
+   `sub/dir` in `HEAD`).  You don't want to give a tree that is not at the
    root level (e.g. `git ls-tree -r HEAD:sub dir`) in this case, as that
-   would result in asking for 'sub/sub/dir' in the `HEAD` commit.
+   would result in asking for `sub/sub/dir` in the `HEAD` commit.
    However, the current working directory can be ignored by passing
    --full-tree option.
 
index 4cc86469f3dd45564b40b327d1b39f2bb213bb98..6294dbc09d213815fb3a31328de87af52fcf606a 100644 (file)
@@ -83,7 +83,8 @@ invocations. The automated message can include the branch description.
 If `--log` is specified, a shortlog of the commits being merged
 will be appended to the specified message.
 
---[no-]rerere-autoupdate::
+--rerere-autoupdate::
+--no-rerere-autoupdate::
        Allow the rerere mechanism to update the index with the
        result of auto-conflict resolution if possible.
 
index df2b64dbb618e3cf7c538798f5f18ebc41e7eff6..f56a5a91975d592b19d03edafef943519718c588 100644 (file)
@@ -146,7 +146,7 @@ OPTIONS
 
 -C <object>::
 --reuse-message=<object>::
-       Take the given blob object (for example, another note) as the
+       Take the given blob object (for example, another note) as the
        note message. (Use `git notes copy <object>` instead to
        copy notes between objects.)
 
index 5c70bc2878fc2f68698b931c6bf951d0097ef813..7061d6634ed422b99495404b3adba9eb04ca7d39 100644 (file)
@@ -38,8 +38,9 @@ OPTIONS
        started.
 
 --reset::
-        Same as -m, except that unmerged entries are discarded
-        instead of failing.
+       Same as -m, except that unmerged entries are discarded instead
+       of failing. When used with `-u`, updates leading to loss of
+       working tree changes will not abort the operation.
 
 -u::
        After a successful merge, update the files in the work
index 6363d674b775fd49b63fcdd3ceb42440a35dd959..f5e6ae3907bae3af8a86ae5c43775f9b39c48e47 100644 (file)
@@ -300,6 +300,11 @@ See also INCOMPATIBLE OPTIONS below.
 +
 See also INCOMPATIBLE OPTIONS below.
 
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+       Allow the rerere mechanism to update the index with the
+       result of auto-conflict resolution if possible.
+
 -S[<keyid>]::
 --gpg-sign[=<keyid>]::
        GPG-sign commits. The `keyid` argument is optional and
@@ -415,9 +420,9 @@ i.e. commits that would be excluded by linkgit:git-log[1]'s
 the `rebase-cousins` mode is turned on, such commits are instead rebased
 onto `<upstream>` (or `<onto>`, if specified).
 +
-The `--rebase-merges` mode is similar in spirit to `--preserve-merges`, but
-in contrast to that option works well in interactive rebases: commits can be
-reordered, inserted and dropped at will.
+The `--rebase-merges` mode is similar in spirit to the deprecated
+`--preserve-merges`, but in contrast to that option works well in interactive
+rebases: commits can be reordered, inserted and dropped at will.
 +
 It is currently only possible to recreate the merge commits using the
 `recursive` merge strategy; Different merge strategies can be used only via
@@ -427,9 +432,10 @@ See also REBASING MERGES and INCOMPATIBLE OPTIONS below.
 
 -p::
 --preserve-merges::
-       Recreate merge commits instead of flattening the history by replaying
-       commits a merge commit introduces. Merge conflict resolutions or manual
-       amendments to merge commits are not preserved.
+       [DEPRECATED: use `--rebase-merges` instead] Recreate merge commits
+       instead of flattening the history by replaying commits a merge commit
+       introduces. Merge conflict resolutions or manual amendments to merge
+       commits are not preserved.
 +
 This uses the `--interactive` machinery internally, but combining it
 with the `--interactive` option explicitly is generally not a good
@@ -1020,11 +1026,11 @@ merge cmake
 
 BUGS
 ----
-The todo list presented by `--preserve-merges --interactive` does not
-represent the topology of the revision graph.  Editing commits and
-rewording their commit messages should work fine, but attempts to
-reorder commits tend to produce counterintuitive results. Use
-`--rebase-merges` in such scenarios instead.
+The todo list presented by the deprecated `--preserve-merges --interactive`
+does not represent the topology of the revision graph (use `--rebase-merges`
+instead).  Editing commits and rewording their commit messages should work
+fine, but attempts to reorder commits tend to produce counterintuitive results.
+Use `--rebase-merges` in such scenarios instead.
 
 For example, an attempt to rearrange
 ------------
index 3fc5d94336f7c706bab12513e14744947a0bcae2..88ea7e1cc01201ccf68ec1e2b8ccd2cda633903a 100644 (file)
@@ -104,7 +104,7 @@ begins with `ext::`.  Examples:
        link-level address).
 
 "ext::git-server-alias foo %G/repo% with% spaces %Vfoo"::
-       Represents a repository with path '/repo with spaces' accessed
+       Represents a repository with path `/repo with spaces` accessed
        using the helper program "git-server-alias foo".  The hostname for
        the remote server passed in the protocol stream will be "foo"
        (this allows multiple virtual Git servers to share a
@@ -118,7 +118,7 @@ begins with `ext::`.  Examples:
 
 SEE ALSO
 --------
-linkgit:gitremote-helpers[1]
+linkgit:gitremote-helpers[7]
 
 GIT
 ---
index 80afca866c0f819ca4b66a9c8a311859e875ffa8..0451ceb8a26dfc0b4ef06dced69a2005bc4e5448 100644 (file)
@@ -52,7 +52,7 @@ EXAMPLES
 
 SEE ALSO
 --------
-linkgit:gitremote-helpers[1]
+linkgit:gitremote-helpers[7]
 
 GIT
 ---
index 49233f5d269f84aeae47f2160cf3b51666a94d4e..6f353ebfd3749a2502adfdbc3d62d12a8432e7c2 100644 (file)
@@ -1,7 +1,7 @@
 git-remote-helpers
 ==================
 
-This document has been moved to linkgit:gitremote-helpers[1].
+This document has been moved to linkgit:gitremote-helpers[7].
 
 Please let the owners of the referring site know so that they can update the
 link you clicked to get here.
diff --git a/Documentation/git-remote-testgit.txt b/Documentation/git-remote-testgit.txt
deleted file mode 100644 (file)
index f791d73..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-git-remote-testgit(1)
-=====================
-
-NAME
-----
-git-remote-testgit - Example remote-helper
-
-
-SYNOPSIS
---------
-[verse]
-git clone testgit::<source-repo> [<destination>]
-
-DESCRIPTION
------------
-
-This command is a simple remote-helper, that is used both as a
-testcase for the remote-helper functionality, and as an example to
-show remote-helper authors one possible implementation.
-
-The best way to learn more is to read the comments and source code in
-'git-remote-testgit'.
-
-SEE ALSO
---------
-linkgit:gitremote-helpers[1]
-
-GIT
----
-Part of the linkgit:git[1] suite
index 132f8e55f67b2e2a40f0058888f4f8260c51f270..26e746c53f25ce833e7dace94b37b178aca48f4e 100644 (file)
@@ -428,8 +428,8 @@ working index HEAD target         working index HEAD
 
 `reset --merge` is meant to be used when resetting out of a conflicted
 merge. Any mergy operation guarantees that the working tree file that is
-involved in the merge does not have local change wrt the index before
-it starts, and that it writes the result out to the working tree. So if
+involved in the merge does not have a local change with respect to the index
+before it starts, and that it writes the result out to the working tree. So if
 we see some difference between the index and the target and also
 between the index and the working tree, then it means that we are not
 resetting out from a state that a mergy operation left after failing
index 837707a8fdb1760f224cc777f6b2b833a6b90a79..6afccb2f1e29fe588c11e979e0d019edc0596b39 100644 (file)
@@ -101,6 +101,11 @@ effect to your index in a row.
        Pass the merge strategy-specific option through to the
        merge strategy.  See linkgit:git-merge[1] for details.
 
+--rerere-autoupdate::
+--no-rerere-autoupdate::
+       Allow the rerere mechanism to update the index with the
+       result of auto-conflict resolution if possible.
+
 SEQUENCER SUBCOMMANDS
 ---------------------
 include::sequencer.txt[]
index 7ef8c4791177b2b54c61573c044659fbbacea9b8..e31ea7d3037d55207132fc6ab07b52b7710199af 100644 (file)
@@ -9,7 +9,7 @@ SYNOPSIS
 --------
 [verse]
 'git stash' list [<options>]
-'git stash' show [<stash>]
+'git stash' show [<options>] [<stash>]
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
@@ -106,7 +106,7 @@ stash@{1}: On master: 9cc0589... Add git-stash
 The command takes options applicable to the 'git log'
 command to control what is shown and how. See linkgit:git-log[1].
 
-show [<stash>]::
+show [<options>] [<stash>]::
 
        Show the changes recorded in the stash entry as a diff between the
        stashed contents and the commit back when the stash entry was first
index 861d821d7f26ec88818008f4c6825bfcad2590ce..d4e8f24f0c86a6998a6190d5c5bfa542c1ef0f4f 100644 (file)
@@ -278,7 +278,8 @@ Header lines start with "#" and are added in response to specific
 command line arguments.  Parsers should ignore headers they
 don't recognize.
 
-### Branch Headers
+Branch Headers
+^^^^^^^^^^^^^^
 
 If `--branch` is given, a series of header lines are printed with
 information about the current branch.
@@ -294,7 +295,8 @@ Line                                     Notes
 ------------------------------------------------------------
 ....
 
-### Changed Tracked Entries
+Changed Tracked Entries
+^^^^^^^^^^^^^^^^^^^^^^^
 
 Following the headers, a series of lines are printed for tracked
 entries.  One of three different line formats may be used to describe
@@ -365,7 +367,8 @@ Field       Meaning
 --------------------------------------------------------
 ....
 
-### Other Items
+Other Items
+^^^^^^^^^^^
 
 Following the tracked entries (and if requested), a series of
 lines will be printed for untracked and then ignored items
@@ -379,7 +382,8 @@ Ignored items have the following format:
 
     ! <path>
 
-### Pathname Format Notes and -z
+Pathname Format Notes and -z
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 When the `-z` option is given, pathnames are printed as is and
 without any quoting and lines are terminated with a NUL (ASCII 0x00)
index 2794e2978021c08b613b07aebf36090c7943fe09..ac8c6879dda34d1efde94903703e19f09a8af891 100644 (file)
@@ -42,7 +42,7 @@ This may be either an absolute URL, or (if it begins with ./
 or ../), the location relative to the superproject's default remote
 repository (Please note that to specify a repository 'foo.git'
 which is located right next to a superproject 'bar.git', you'll
-have to use '../foo.git' instead of './foo.git' - as one might expect
+have to use `../foo.git` instead of `./foo.git` - as one might expect
 when following the rules for relative URLs - because the evaluation
 of relative URLs in Git is identical to that of relative directories).
 +
index b99029520d9b17e76ab65b4333a79fe20ed22eff..223788fa3ee070b2919a01e1b4eb4cc55043b2af 100644 (file)
@@ -126,7 +126,7 @@ your Perl's Getopt::Long is < v2.37).
        command-line argument.
 +
 This automatically updates the rev_map if needed (see
-'$GIT_DIR/svn/\*\*/.rev_map.*' in the FILES section below for details).
+'$GIT_DIR/svn/\**/.rev_map.*' in the FILES section below for details).
 
 --localtime;;
        Store Git commit times in the local time zone instead of UTC.  This
@@ -239,7 +239,7 @@ Like 'git rebase'; this requires that the working tree be clean
 and have no uncommitted changes.
 +
 This automatically updates the rev_map if needed (see
-'$GIT_DIR/svn/\*\*/.rev_map.*' in the FILES section below for details).
+'$GIT_DIR/svn/\**/.rev_map.*' in the FILES section below for details).
 
 -l;;
 --local;;
@@ -524,7 +524,7 @@ This will set the property 'svn:keywords' to 'FreeBSD=%H' for the file
        way to repair the repo is to use 'reset'.
 +
 Only the rev_map and refs/remotes/git-svn are changed (see
-'$GIT_DIR/svn/\*\*/.rev_map.*' in the FILES section below for details).
+'$GIT_DIR/svn/\**/.rev_map.*' in the FILES section below for details).
 Follow 'reset' with a 'fetch' and then 'git reset' or 'git rebase' to
 move local branches onto the new tree.
 
@@ -760,7 +760,7 @@ svn-remote.<name>.noMetadata::
 +
 This option can only be used for one-shot imports as 'git svn'
 will not be able to fetch again without metadata. Additionally,
-if you lose your '$GIT_DIR/svn/\*\*/.rev_map.*' files, 'git svn' will not
+if you lose your '$GIT_DIR/svn/\**/.rev_map.*' files, 'git svn' will not
 be able to rebuild them.
 +
 The 'git svn log' command will not work on repositories using
@@ -1154,7 +1154,7 @@ fetching, then $GIT_DIR/svn/.metadata must be manually edited to remove
 
 FILES
 -----
-$GIT_DIR/svn/\*\*/.rev_map.*::
+$GIT_DIR/svn/\**/.rev_map.*::
        Mapping between Subversion revision numbers and Git commit
        names.  In a repository where the noMetadata option is not set,
        this can be rebuilt from the git-svn-id: lines that are at the
index fd952a5ff92b72e67eac219fdfbbe5d7c708be81..8d162b56c5901f22e3eb1af5faf6cb54c68ba6d7 100644 (file)
@@ -92,8 +92,8 @@ configuration variable, we launch 'kfmclient' to try to open the HTML
 man page on an already opened konqueror in a new tab if possible.
 
 For consistency, we also try such a trick if 'browser.konqueror.path' is
-set to something like 'A_PATH_TO/konqueror'. That means we will try to
-launch 'A_PATH_TO/kfmclient' instead.
+set to something like `A_PATH_TO/konqueror`. That means we will try to
+launch `A_PATH_TO/kfmclient` instead.
 
 If you really want to use 'konqueror', then you can use something like
 the following:
index cb86318f3e1b34e0eda5b9886fc934456434e7a8..85d92c9761da226010d1fffe20bbbfc404680c66 100644 (file)
@@ -213,7 +213,7 @@ refs of one working tree from another.
 
 In general, all pseudo refs are per working tree and all refs starting
 with "refs/" are shared. Pseudo refs are ones like HEAD which are
-directly under GIT_DIR instead of inside GIT_DIR/refs. There are one
+directly under GIT_DIR instead of inside GIT_DIR/refs. There is one
 exception to this: refs inside refs/bisect and refs/worktree is not
 shared.
 
index 00156d64aad51cd758e554ada68a52a3e55dfc0c..6d1f2fd9ae9e993e46d935f835bdfec4e6f686e1 100644 (file)
@@ -536,7 +536,6 @@ other
        The command-line parameters passed to the configured command are
        determined by the ssh variant.  See `ssh.variant` option in
        linkgit:git-config[1] for details.
-
 +
 `$GIT_SSH_COMMAND` takes precedence over `$GIT_SSH`, and is interpreted
 by the shell, which allows additional arguments to be included.
index bdd11a2dddf81647937f1cf002b37e0b192b47d1..4fb20cd0e963b6cf52c07c6ae7492adbf35f6cbf 100644 (file)
@@ -18,7 +18,7 @@ A `gitattributes` file is a simple text file that gives
 
 Each line in `gitattributes` file is of form:
 
-       pattern attr1 attr2 ...
+       pattern attr1 attr2 ...
 
 That is, a pattern followed by an attributes list,
 separated by whitespaces. Leading and trailing whitespaces are
@@ -314,8 +314,8 @@ stored as UTF-8 internally. A client without `working-tree-encoding`
 support will checkout `foo.ps1` as UTF-8 encoded file. This will
 typically cause trouble for the users of this file.
 +
-If a Git client, that does not support the `working-tree-encoding`
-attribute, adds a new file `bar.ps1`, then `bar.ps1` will be
+If a Git client that does not support the `working-tree-encoding`
+attribute adds a new file `bar.ps1`, then `bar.ps1` will be
 stored "as-is" internally (in this example probably as UTF-16).
 A client with `working-tree-encoding` support will interpret the
 internal contents as UTF-8 and try to convert it to UTF-16 on checkout.
index 5bf653c111d07e0958840e38187320a433b20d07..786e778ab8223a0ee02a44c8c756652fcf147205 100644 (file)
@@ -496,6 +496,24 @@ This hook is invoked by `git-p4 submit`. It takes no parameters and nothing
 from standard input. Exiting with non-zero status from this script prevent
 `git-p4 submit` from launching. Run `git-p4 submit --help` for details.
 
+post-index-change
+~~~~~~~~~~~~~~~~~
+
+This hook is invoked when the index is written in read-cache.c
+do_write_locked_index.
+
+The first parameter passed to the hook is the indicator for the
+working directory being updated.  "1" meaning working directory
+was updated or "0" when the working directory was not updated.
+
+The second parameter passed to the hook is the indicator for whether
+or not the index was updated and the skip-worktree bit could have
+changed.  "1" meaning skip-worktree bits could have been updated
+and "0" meaning they were not.
+
+Only one parameter should be set to "1" when the hook runs.  The hook
+running passing "1", "1" should not be possible.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 1c94f08ff4dd9af173c659fb0b536f6a71a1c252..b5bc9dbff05b293caae7c42e865b19bc067c4d82 100644 (file)
@@ -132,6 +132,14 @@ full pathname may have special meaning:
  - Other consecutive asterisks are considered regular asterisks and
    will match according to the previous rules.
 
+CONFIGURATION
+-------------
+
+The optional configuration variable `core.excludesFile` indicates a path to a
+file containing patterns of file names to exclude, similar to
+`$GIT_DIR/info/exclude`.  Patterns in the exclude file are used in addition to
+those in `$GIT_DIR/info/exclude`.
+
 NOTES
 -----
 
index 244cd01493127035b27fb19416f1f4027a726bba..1eabb0aaf3aec94f7551b259efff7ba317ce610e 100644 (file)
@@ -168,12 +168,12 @@ Files
 -----
 User configuration and preferences are stored at:
 
-* '$XDG_CONFIG_HOME/git/gitk' if it exists, otherwise
-* '$HOME/.gitk' if it exists
+* `$XDG_CONFIG_HOME/git/gitk` if it exists, otherwise
+* `$HOME/.gitk` if it exists
 
-If neither of the above exist then '$XDG_CONFIG_HOME/git/gitk' is created and
+If neither of the above exist then `$XDG_CONFIG_HOME/git/gitk` is created and
 used by default. If '$XDG_CONFIG_HOME' is not set it defaults to
-'$HOME/.config' in all cases.
+`$HOME/.config` in all cases.
 
 History
 -------
index 312b6f92592258b88efcaed36392df998bfaba0a..a66e95b70c363a35dc1fd5413c7063f6372b4b0a 100644 (file)
@@ -115,7 +115,7 @@ Consider the following .gitmodules file:
 
 
 This defines two submodules, `libfoo` and `libbar`. These are expected to
-be checked out in the paths 'include/foo' and 'include/bar', and for both
+be checked out in the paths `include/foo` and `include/bar`, and for both
 submodules a URL is specified which can be used for cloning the submodules.
 
 SEE ALSO
index 9d1459aac6d0b12ad1a87ff25a158dca0f2bf470..43f80c80683805f97a71220005af7a0e862b8cca 100644 (file)
@@ -1,4 +1,4 @@
-gitremote-helpers(1)
+gitremote-helpers(7)
 ====================
 
 NAME
@@ -468,7 +468,7 @@ set by Git if the remote helper has the 'option' capability.
 
 'option dry-run' {'true'|'false'}:
        If true, pretend the operation completed successfully,
-       but don't actually change any repository data.  For most
+       but don't actually change any repository data.  For most
        helpers this only applies to the 'push', if supported.
 
 'option servpath <c-style-quoted-path>'::
@@ -513,8 +513,6 @@ linkgit:git-remote-ext[1]
 
 linkgit:git-remote-fd[1]
 
-linkgit:git-remote-testgit[1]
-
 linkgit:git-fast-import[1]
 
 GIT
index 366dee238c22486a5a934c4184eaafb1723db4e2..216b11ee88f4e9c5b10278e037f3b1c51173bc6d 100644 (file)
@@ -84,7 +84,7 @@ objects/info/alternates::
        to the object database, not to the repository!) in your
        alternates file, but it will not work if you use absolute
        paths unless the absolute path in filesystem and web URL
-       is the same. See also 'objects/info/http-alternates'.
+       is the same. See also `objects/info/http-alternates`.
 
 objects/info/http-alternates::
        This file records URLs to alternate object stores that
index 92535dbac53ac49d89ef6d8ce62dd2920c83c445..35317e71c870dec2f6ea6db6a262400e471e3ba1 100644 (file)
@@ -50,11 +50,11 @@ following order:
  * built-in values (some set during build stage),
 
  * common system-wide configuration file (defaults to
-   '/etc/gitweb-common.conf'),
+   `/etc/gitweb-common.conf`),
 
  * either per-instance configuration file (defaults to 'gitweb_config.perl'
    in the same directory as the installed gitweb), or if it does not exists
-   then fallback system-wide configuration file (defaults to '/etc/gitweb.conf').
+   then fallback system-wide configuration file (defaults to `/etc/gitweb.conf`).
 
 Values obtained in later configuration files override values obtained earlier
 in the above sequence.
@@ -82,7 +82,7 @@ You can include other configuration file using read_config_file()
 subroutine.  For example, one might want to put gitweb configuration
 related to access control for viewing repositories via Gitolite (one
 of Git repository management tools) in a separate file, e.g. in
-'/etc/gitweb-gitolite.conf'.  To include it, put
+`/etc/gitweb-gitolite.conf`.  To include it, put
 
 --------------------------------------------------
 read_config_file("/etc/gitweb-gitolite.conf");
@@ -142,7 +142,7 @@ and its path_info based equivalent
 http://git.example.com/gitweb.cgi/foo/bar.git
 ------------------------------------------------
 +
-will map to the path '/srv/git/foo/bar.git' on the filesystem.
+will map to the path `/srv/git/foo/bar.git` on the filesystem.
 
 $projects_list::
        Name of a plain text file listing projects, or a name of directory
@@ -234,9 +234,9 @@ $GIT::
 
 $mimetypes_file::
        File to use for (filename extension based) guessing of MIME types before
-       trying '/etc/mime.types'.  *NOTE* that this path, if relative, is taken
+       trying `/etc/mime.types`.  *NOTE* that this path, if relative, is taken
        as relative to the current Git repository, not to CGI script.  If unset,
-       only '/etc/mime.types' is used (if present on filesystem).  If no mimetypes
+       only `/etc/mime.types` is used (if present on filesystem).  If no mimetypes
        file is found, mimetype guessing based on extension of file is disabled.
        Unset by default.
 
@@ -297,8 +297,8 @@ relative to base URI of gitweb.
 +
 This list should contain the URI of gitweb's standard stylesheet.  The default
 URI of gitweb stylesheet can be set at build time using the `GITWEB_CSS`
-makefile variable.  Its default value is 'static/gitweb.css'
-(or 'static/gitweb.min.css' if the `CSSMIN` variable is defined,
+makefile variable.  Its default value is `static/gitweb.css`
+(or `static/gitweb.min.css` if the `CSSMIN` variable is defined,
 i.e. if CSS minifier is used during build).
 +
 *Note*: there is also a legacy `$stylesheet` configuration variable, which was
@@ -311,7 +311,7 @@ $logo::
        is displayed in the top right corner of each gitweb page and used as
        a logo for the Atom feed.  Relative to the base URI of gitweb (as a path).
        Can be adjusted when building gitweb using `GITWEB_LOGO` variable
-       By default set to 'static/git-logo.png'.
+       By default set to `static/git-logo.png`.
 
 $favicon::
        Points to the location where you put 'git-favicon.png' on your web
@@ -320,7 +320,7 @@ $favicon::
        may display them in the browser's URL bar and next to the site name in
        bookmarks.  Relative to the base URI of gitweb.  Can be adjusted at
        build time using `GITWEB_FAVICON` variable.
-       By default set to 'static/git-favicon.png'.
+       By default set to `static/git-favicon.png`.
 
 $javascript::
        Points to the location where you put 'gitweb.js' on your web server,
@@ -328,7 +328,7 @@ $javascript::
        Relative to the base URI of gitweb.  Can be set at build time using
        the `GITWEB_JS` build-time configuration variable.
 +
-The default value is either 'static/gitweb.js', or 'static/gitweb.min.js' if
+The default value is either `static/gitweb.js`, or `static/gitweb.min.js` if
 the `JSMIN` build variable was defined, i.e. if JavaScript minifier was used
 at build time.  *Note* that this single file is generated from multiple
 individual JavaScript "modules".
@@ -444,7 +444,7 @@ $default_blob_plain_mimetype::
        doesn't result in some other type; by default "text/plain".
        Gitweb guesses mimetype of a file to display based on extension
        of its filename, using `$mimetypes_file` (if set and file exists)
-       and '/etc/mime.types' files (see *mime.types*(5) manpage; only
+       and `/etc/mime.types` files (see *mime.types*(5) manpage; only
        filename extension rules are supported by gitweb).
 
 $default_text_plain_charset::
@@ -486,7 +486,7 @@ affects how "summary" pages look like, or load limiting).
        (for example one for `git://` protocol, and one for `http://`
        protocol).
 +
-Note that per repository configuration can be set in '$GIT_DIR/cloneurl'
+Note that per repository configuration can be set in `$GIT_DIR/cloneurl`
 file, or as values of multi-value `gitweb.url` configuration variable in
 project config.  Per-repository configuration takes precedence over value
 composed from `@git_base_url_list` elements and project name.
@@ -520,7 +520,7 @@ $maxload::
        If the server load exceeds this value then gitweb will return
        "503 Service Unavailable" error.  The server load is taken to be 0
        if gitweb cannot determine its value.  Currently it works only on Linux,
-       where it uses '/proc/loadavg'; the load there is the number of active
+       where it uses `/proc/loadavg`; the load there is the number of active
        tasks on the system -- processes that are actually running -- averaged
        over the last minute.
 +
@@ -536,7 +536,7 @@ $omit_owner::
 
 $per_request_config::
        If this is set to code reference, it will be run once for each request.
-       You can set parts of configuration that change per session this way.
+       You can set parts of configuration that change per session this way.
        For example, one might use the following code in a gitweb configuration
        file
 +
@@ -739,7 +739,7 @@ Currently available providers are *"gravatar"* and *"picon"*.
 Only one provider at a time can be selected ('default' is one element list).
 If an unknown provider is specified, the feature is disabled.
 *Note* that some providers might require extra Perl packages to be
-installed; see 'gitweb/INSTALL' for more details.
+installed; see `gitweb/INSTALL` for more details.
 +
 This feature can be configured on a per-repository basis via
 repository's `gitweb.avatar` configuration variable.
index 88450589aff7e656a5fbf52a36da433d1627d177..c7436098c9596f221fc9553c1af7f8a599c6301c 100644 (file)
@@ -36,7 +36,7 @@ browsed using gitweb itself.
 CONFIGURATION
 -------------
 Various aspects of gitweb's behavior can be controlled through the configuration
-file 'gitweb_config.perl' or '/etc/gitweb.conf'.  See the linkgit:gitweb.conf[5]
+file `gitweb_config.perl` or `/etc/gitweb.conf`.  See the linkgit:gitweb.conf[5]
 for details.
 
 Repositories
@@ -51,7 +51,7 @@ projects' root" subsection).
 our $projectroot = '/path/to/parent/directory';
 -----------------------------------------------------------------------
 
-The default value for `$projectroot` is '/pub/git'.  You can change it during
+The default value for `$projectroot` is `/pub/git`.  You can change it during
 building gitweb via `GITWEB_PROJECTROOT` build configuration variable.
 
 By default all Git repositories under `$projectroot` are visible and available
@@ -231,7 +231,7 @@ Unnamed repository; edit this file to name it for gitweb.
 -------------------------------------------------------------------------------
 +
 from the template during repository creation, usually installed in
-'/usr/share/git-core/templates/'.  You can use the `gitweb.description` repo
+`/usr/share/git-core/templates/`.  You can use the `gitweb.description` repo
 configuration variable, but the file takes precedence.
 
 category (or `gitweb.category`)::
@@ -407,7 +407,7 @@ in the instructions so they can be included in a future release.
 Apache as CGI
 ~~~~~~~~~~~~~
 Apache must be configured to support CGI scripts in the directory in
-which gitweb is installed.  Let's assume that it is '/var/www/cgi-bin'
+which gitweb is installed.  Let's assume that it is `/var/www/cgi-bin`
 directory.
 
 -----------------------------------------------------------------------
@@ -431,7 +431,7 @@ You can use mod_perl with gitweb.  You must install Apache::Registry
 (for mod_perl 1.x) or ModPerl::Registry (for mod_perl 2.x) to enable
 this support.
 
-Assuming that gitweb is installed to '/var/www/perl', the following
+Assuming that gitweb is installed to `/var/www/perl`, the following
 Apache configuration (for mod_perl 2.x) is suitable.
 
 -----------------------------------------------------------------------
@@ -456,7 +456,7 @@ Apache with FastCGI
 ~~~~~~~~~~~~~~~~~~~
 Gitweb works with Apache and FastCGI.  First you need to rename, copy
 or symlink gitweb.cgi to gitweb.fcgi.  Let's assume that gitweb is
-installed in '/usr/share/gitweb' directory.  The following Apache
+installed in `/usr/share/gitweb` directory.  The following Apache
 configuration is suitable (UNTESTED!)
 
 -----------------------------------------------------------------------
@@ -503,22 +503,22 @@ repositories, you can configure Apache like this:
 -----------------------------------------------------------------------
 
 The above configuration expects your public repositories to live under
-'/pub/git' and will serve them as `http://git.domain.org/dir-under-pub-git`,
+`/pub/git` and will serve them as `http://git.domain.org/dir-under-pub-git`,
 both as clonable Git URL and as browseable gitweb interface.  If you then
 start your linkgit:git-daemon[1] with `--base-path=/pub/git --export-all`
 then you can even use the `git://` URL with exactly the same path.
 
 Setting the environment variable `GITWEB_CONFIG` will tell gitweb to use the
-named file (i.e. in this example '/etc/gitweb.conf') as a configuration for
+named file (i.e. in this example `/etc/gitweb.conf`) as a configuration for
 gitweb.  You don't really need it in above example; it is required only if
 your configuration file is in different place than built-in (during
-compiling gitweb) 'gitweb_config.perl' or '/etc/gitweb.conf'.  See
+compiling gitweb) 'gitweb_config.perl' or `/etc/gitweb.conf`.  See
 linkgit:gitweb.conf[5] for details, especially information about precedence
 rules.
 
 If you use the rewrite rules from the example you *might* also need
 something like the following in your gitweb configuration file
-('/etc/gitweb.conf' following example):
+(`/etc/gitweb.conf` following example):
 ----------------------------------------------------------------------------
 @stylesheets = ("/some/absolute/path/gitweb.css");
 $my_uri    = "/";
@@ -575,7 +575,7 @@ like this:
 
 Here actual project root is passed to gitweb via `GITWEB_PROJECT_ROOT`
 environment variable from a web server, so you need to put the following
-line in gitweb configuration file ('/etc/gitweb.conf' in above example):
+line in gitweb configuration file (`/etc/gitweb.conf` in above example):
 --------------------------------------------------------------------------
 $projectroot = $ENV{'GITWEB_PROJECTROOT'} || "/pub/git";
 --------------------------------------------------------------------------
@@ -585,7 +585,7 @@ referenced by `$per_request_config`;
 
 These configurations enable two things. First, each unix user (`<user>`) of
 the server will be able to browse through gitweb Git repositories found in
-'~/public_git/' with the following url:
+`~/public_git/` with the following url:
 
   http://git.example.org/~<user>/
 
@@ -596,7 +596,7 @@ If you already use `mod_userdir` in your virtual host or you don't want to
 use the \'~' as first character, just comment or remove the second rewrite
 rule, and uncomment one of the following according to what you want.
 
-Second, repositories found in '/pub/scm/' and '/var/git/' will be accessible
+Second, repositories found in `/pub/scm/` and `/var/git/` will be accessible
 through `http://git.example.org/scm/` and `http://git.example.org/var/`.
 You can add as many project roots as you want by adding rewrite rules like
 the third and the fourth.
@@ -614,7 +614,7 @@ that it consumes and produces URLs in the form
   http://git.example.com/project.git/shortlog/sometag
 
 i.e. without 'gitweb.cgi' part, by using a configuration such as the
-following.  This configuration assumes that '/var/www/gitweb' is the
+following.  This configuration assumes that `/var/www/gitweb` is the
 DocumentRoot of your webserver, contains the gitweb.cgi script and
 complementary static files (stylesheet, favicon, JavaScript):
 
@@ -645,9 +645,9 @@ parameter.
 `@stylesheets`, `$my_uri` and `$home_link`, but you lose "dumb client"
 access to your project .git dirs (described in "Single URL for gitweb and
 for fetching" section).  A possible workaround for the latter is the
-following: in your project root dir (e.g. '/pub/git') have the projects
-named *without* a .git extension (e.g. '/pub/git/project' instead of
-'/pub/git/project.git') and configure Apache as follows:
+following: in your project root dir (e.g. `/pub/git`) have the projects
+named *without* a .git extension (e.g. `/pub/git/project` instead of
+`/pub/git/project.git`) and configure Apache as follows:
 ----------------------------------------------------------------------------
 <VirtualHost *:80>
        ServerAlias git.example.com
@@ -681,7 +681,7 @@ cloned), while
 will provide human-friendly gitweb access.
 
 This solution is not 100% bulletproof, in the sense that if some project has
-a named ref (branch, tag) starting with 'git/', then paths such as
+a named ref (branch, tag) starting with `git/`, then paths such as
 
   http://git.example.com/project/command/abranch..git/abranch
 
@@ -697,7 +697,7 @@ SEE ALSO
 --------
 linkgit:gitweb.conf[5], linkgit:git-instaweb[1]
 
-'gitweb/README', 'gitweb/INSTALL'
+`gitweb/README`, `gitweb/INSTALL`
 
 GIT
 ---
index 023ca95e7c39d88fcac832d73a7454e9fb71e5a1..8d38ae6010866a157dfdb7d52b58605d1cd2f3b6 100644 (file)
@@ -287,6 +287,15 @@ This commit is referred to as a "merge commit", or sometimes just a
        origin/name-of-upstream-branch, which you can see using
        `git branch -r`.
 
+[[def_overlay]]overlay::
+       Only update and add files to the working directory, but don't
+       delete them, similar to how 'cp -R' would update the contents
+       in the destination directory.  This is the default mode in a
+       <<def_checkout,checkout>> when checking out files from the
+       <<def_index,index>> or a <<def_tree-ish,tree-ish>>.  In
+       contrast, no-overlay mode also deletes tracked files not
+       present in the source, similar to 'rsync --delete'.
+
 [[def_pack]]pack::
        A set of objects which have been compressed into one file (to save space
        or to transmit them efficiently).
index f44e5e9458592a0554e4e2e163a6b029c1435f80..bfe6f9b5006335e96ecb4def4872ec119f3a3b0f 100644 (file)
@@ -244,8 +244,8 @@ Using a proxy:
 --------------
 
 If you have to access the WebDAV server from behind an HTTP(S) proxy,
-set the variable 'all_proxy' to 'http://proxy-host.com:port', or
-'http://login-on-proxy:passwd-on-proxy@proxy-host.com:port'. See 'man
+set the variable 'all_proxy' to `http://proxy-host.com:port`, or
+`http://login-on-proxy:passwd-on-proxy@proxy-host.com:port`. See 'man
 curl' for details.
 
 
index ca959a72862048c205d67576fcd6c538f0c91b26..ddbc1de43f87539b8be945ebfd42e6562ff097a6 100644 (file)
@@ -743,7 +743,7 @@ explicitly-given commit or tree.
 
 --filter-print-omitted::
        Only useful with `--filter=`; prints a list of the objects omitted
-       by the filter.  Object IDs are prefixed with a ``~'' character.
+       by the filter.  Object IDs are prefixed with a ``~'' character.
 
 --missing=<missing-action>::
        A debug option to help with future "partial clone" development.
@@ -805,12 +805,13 @@ include::pretty-options.txt[]
        author's). If `-local` is appended to the format (e.g.,
        `iso-local`), the user's local time zone is used instead.
 +
+--
 `--date=relative` shows dates relative to the current time,
 e.g. ``2 hours ago''. The `-local` option has no effect for
 `--date=relative`.
-+
+
 `--date=local` is an alias for `--date=default-local`.
-+
+
 `--date=iso` (or `--date=iso8601`) shows timestamps in a ISO 8601-like format.
 The differences to the strict ISO 8601 format are:
 
@@ -818,15 +819,14 @@ The differences to the strict ISO 8601 format are:
        - a space between time and time zone
        - no colon between hours and minutes of the time zone
 
-+
 `--date=iso-strict` (or `--date=iso8601-strict`) shows timestamps in strict
 ISO 8601 format.
-+
+
 `--date=rfc` (or `--date=rfc2822`) shows timestamps in RFC 2822
 format, often found in email messages.
-+
+
 `--date=short` shows only the date, but not the time, in `YYYY-MM-DD` format.
-+
+
 `--date=raw` shows the date as seconds since the epoch (1970-01-01
 00:00:00 UTC), followed by a space, and then the timezone as an offset
 from UTC (a `+` or `-` with four digits; the first two are hours, and
@@ -835,28 +835,28 @@ with `strftime("%s %z")`).
 Note that the `-local` option does not affect the seconds-since-epoch
 value (which is always measured in UTC), but does switch the accompanying
 timezone value.
-+
+
 `--date=human` shows the timezone if the timezone does not match the
 current time-zone, and doesn't print the whole date if that matches
 (ie skip printing year for dates that are "this year", but also skip
 the whole date itself if it's in the last few days and we can just say
 what weekday it was).  For older dates the hour and minute is also
 omitted.
-+
+
 `--date=unix` shows the date as a Unix epoch timestamp (seconds since
 1970).  As with `--raw`, this is always in UTC and therefore `-local`
 has no effect.
-+
+
 `--date=format:...` feeds the format `...` to your system `strftime`,
 except for %z and %Z, which are handled internally.
 Use `--date=format:%c` to show the date in your system locale's
 preferred format.  See the `strftime` manual for a complete list of
 format placeholders. When using `-local`, the correct syntax is
 `--date=format-local:...`.
-+
+
 `--date=default` is the default format, and is similar to
 `--date=rfc2822`, with a few exceptions:
-
+--
        - there is no comma after the day-of-week
 
        - the time zone is omitted when the local time zone is used
index 72daa20e76fa0a0c10feaf37f927797ad1445934..2337a995eccace3af47e98ed006a2ccbd44a4f4e 100644 (file)
@@ -58,7 +58,7 @@ when you run `git merge`.
 when you run `git cherry-pick`.
 +
 Note that any of the 'refs/*' cases above may come either from
-the '$GIT_DIR/refs' directory or from the '$GIT_DIR/packed-refs' file.
+the `$GIT_DIR/refs` directory or from the `$GIT_DIR/packed-refs` file.
 While the ref name encoding is unspecified, UTF-8 is preferred as
 some output processing may assume ref names in UTF-8.
 
@@ -108,7 +108,7 @@ some output processing may assume ref names in UTF-8.
   `git push` were run while `branchname` was checked out (or the current
   `HEAD` if no branchname is specified). Since our push destination is
   in a remote repository, of course, we report the local tracking branch
-  that corresponds to that branch (i.e., something in 'refs/remotes/').
+  that corresponds to that branch (i.e., something in `refs/remotes/`).
 +
 Here's an example to make it more clear:
 +
index 5747f442f25e6271c7464db78496b85ee058d05f..5a57c4a4077f0bba7bb0186932a166f2dc7666ce 100644 (file)
@@ -1,6 +1,6 @@
 --continue::
        Continue the operation in progress using the information in
-       '.git/sequencer'.  Can be used to continue after resolving
+       `.git/sequencer`.  Can be used to continue after resolving
        conflicts in a failed cherry-pick or revert.
 
 --quit::
index fa39ac9d719b57e0df33441b11144bede4822621..7d20716c32a453d0445b37ed19f7d8caf089bec1 100644 (file)
@@ -229,7 +229,7 @@ A `config_set` can be used to construct an in-memory cache for
 config-like files that the caller specifies (i.e., files like `.gitmodules`,
 `~/.gitconfig` etc.). For example,
 
----------------------------------------
+----------------------------------------
 struct config_set gm_config;
 git_configset_init(&gm_config);
 int b;
index 1c0086e287eadec2f6e1ffa1ca0dad49d7bbbe28..844629c8c441e786734c42e749293f2a718ed288 100644 (file)
@@ -20,8 +20,8 @@ More interesting possibilities exist, though, such as:
   * one side of history renames x -> z, and the other renames some file to
     x/e, causing the need for the merge to do a transitive rename.
 
-  * one side of history renames x -> z, but also renames all files within
-    x.  For example, x/a -> z/alpha, x/b -> z/bravo, etc.
+  * one side of history renames x -> z, but also renames all files within x.
+    For example, x/a -> z/alpha, x/b -> z/bravo, etc.
 
   * both 'x' and 'y' being merged into a single directory 'z', with a
     directory rename being detected for both x->z and y->z.
index 7a2375a55d074514c5ebd851fe55ff27541c207c..c73e72de0e9ccef9f5ad5143ae9d208a3fef1a14 100644 (file)
@@ -657,14 +657,14 @@ can be rejected.
 An example client/server communication might look like this:
 
 ----
-   S: 007c74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n
+   S: 006274730d410fcb6603ace96f1dc55ea6196122532d refs/heads/local\0report-status delete-refs ofs-delta\n
    S: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe refs/heads/debug\n
    S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/master\n
-   S: 003f74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n
+   S: 003d74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/team\n
    S: 0000
 
-   C: 003e7d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n
-   C: 003e74730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n
+   C: 00677d1665144a3a975c05f1f43902ddaf084e784dbe 74730d410fcb6603ace96f1dc55ea6196122532d refs/heads/debug\n
+   C: 006874730d410fcb6603ace96f1dc55ea6196122532d 5a3f6be755bbb7deae50065988cbfa1ffa9ab68a refs/heads/master\n
    C: 0000
    C: [PACKDATA]
 
index ead85ce35cf2342335af69221e4b4e956b9f66fd..03264c7d9a833bc4ed8d15be262bf6186a67c892 100644 (file)
@@ -1,5 +1,5 @@
- Git Wire Protocol, Version 2
-==============================
+Git Wire Protocol, Version 2
+============================
 
 This document presents a specification for a version 2 of Git's wire
 protocol.  Protocol v2 will improve upon v1 in the following ways:
@@ -22,8 +22,8 @@ will be commands which a client can request be executed.  Once a command
 has completed, a client can reuse the connection and request that other
 commands be executed.
 
- Packet-Line Framing
----------------------
+Packet-Line Framing
+-------------------
 
 All communication is done using packet-line framing, just as in v1.  See
 `Documentation/technical/pack-protocol.txt` and
@@ -34,8 +34,8 @@ In protocol v2 these special packets will have the following semantics:
   * '0000' Flush Packet (flush-pkt) - indicates the end of a message
   * '0001' Delimiter Packet (delim-pkt) - separates sections of a message
 
- Initial Client Request
-------------------------
+Initial Client Request
+----------------------
 
 In general a client can request to speak protocol v2 by sending
 `version=2` through the respective side-channel for the transport being
@@ -43,22 +43,22 @@ used which inevitably sets `GIT_PROTOCOL`.  More information can be
 found in `pack-protocol.txt` and `http-protocol.txt`.  In all cases the
 response from the server is the capability advertisement.
 
- Git Transport
-~~~~~~~~~~~~~~~
+Git Transport
+~~~~~~~~~~~~~
 
 When using the git:// transport, you can request to use protocol v2 by
 sending "version=2" as an extra parameter:
 
    003egit-upload-pack /project.git\0host=myserver.com\0\0version=2\0
 
- SSH and File Transport
-~~~~~~~~~~~~~~~~~~~~~~~~
+SSH and File Transport
+~~~~~~~~~~~~~~~~~~~~~~
 
 When using either the ssh:// or file:// transport, the GIT_PROTOCOL
 environment variable must be set explicitly to include "version=2".
 
- HTTP Transport
-~~~~~~~~~~~~~~~~
+HTTP Transport
+~~~~~~~~~~~~~~
 
 When using the http:// or https:// transport a client makes a "smart"
 info/refs request as described in `http-protocol.txt` and requests that
@@ -79,8 +79,8 @@ A v2 server would reply:
 Subsequent requests are then made directly to the service
 `$GIT_URL/git-upload-pack`. (This works the same for git-receive-pack).
 
- Capability Advertisement
---------------------------
+Capability Advertisement
+------------------------
 
 A server which decides to communicate (based on a request from a client)
 using protocol version 2, notifies the client by sending a version string
@@ -101,8 +101,8 @@ to be executed by the client.
     key = 1*(ALPHA | DIGIT | "-_")
     value = 1*(ALPHA | DIGIT | " -_.,?\/{}[]()<>!@#$%^&*+=:;")
 
- Command Request
------------------
+Command Request
+---------------
 
 After receiving the capability advertisement, a client can then issue a
 request to select the command it wants with any particular capabilities
@@ -137,8 +137,8 @@ command be executed or can terminate the connection.  A client may
 optionally send an empty request consisting of just a flush-pkt to
 indicate that no more requests will be made.
 
- Capabilities
---------------
+Capabilities
+------------
 
 There are two different types of capabilities: normal capabilities,
 which can be used to to convey information or alter the behavior of a
@@ -153,8 +153,8 @@ management on the server side in order to function correctly.  This
 permits simple round-robin load-balancing on the server side, without
 needing to worry about state management.
 
- agent
-~~~~~~~
+agent
+~~~~~
 
 The server can advertise the `agent` capability with a value `X` (in the
 form `agent=X`) to notify the client that the server is running version
@@ -168,8 +168,8 @@ printable ASCII characters except space (i.e., the byte range 32 < x <
 and debugging purposes, and MUST NOT be used to programmatically assume
 the presence or absence of particular features.
 
- ls-refs
-~~~~~~~~~
+ls-refs
+~~~~~~~
 
 `ls-refs` is the command used to request a reference advertisement in v2.
 Unlike the current reference advertisement, ls-refs takes in arguments
@@ -199,8 +199,8 @@ The output of ls-refs is as follows:
     symref = "symref-target:" symref-target
     peeled = "peeled:" obj-id
 
- fetch
-~~~~~~~
+fetch
+~~~~~
 
 `fetch` is the command used to fetch a packfile in v2.  It can be looked
 at as a modified version of the v1 fetch where the ref-advertisement is
@@ -444,8 +444,8 @@ header.
                2 - progress messages
                3 - fatal error message just before stream aborts
 
- server-option
-~~~~~~~~~~~~~~~
+server-option
+~~~~~~~~~~~~~
 
 If advertised, indicates that any number of server specific options can be
 included in a request.  This is done by sending each option as a
index b05da95788f7b1294be6b2733ca55b58111b7607..bc354fe2dce57bfbbb50aec3fb3231b3ff309dda 100644 (file)
@@ -62,7 +62,7 @@ may be used:
 
 where <address> may be a path, a server and path, or an arbitrary
 URL-like string recognized by the specific remote helper being
-invoked. See linkgit:gitremote-helpers[1] for details.
+invoked. See linkgit:gitremote-helpers[7] for details.
 
 If there are a large number of similarly-named remote repositories and
 you want to use a different format for them (such that the URLs you
index 3e03290d8ff7e81a58e89d6b09e4ad5834af4510..9f1b6e8926682c82530baa597794544b89d88c3a 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -613,10 +613,9 @@ SCRIPT_SH += git-merge-one-file.sh
 SCRIPT_SH += git-merge-resolve.sh
 SCRIPT_SH += git-mergetool.sh
 SCRIPT_SH += git-quiltimport.sh
-SCRIPT_SH += git-legacy-rebase.sh
+SCRIPT_SH += git-legacy-stash.sh
 SCRIPT_SH += git-remote-testgit.sh
 SCRIPT_SH += git-request-pull.sh
-SCRIPT_SH += git-stash.sh
 SCRIPT_SH += git-submodule.sh
 SCRIPT_SH += git-web--browse.sh
 
@@ -1131,6 +1130,7 @@ BUILTIN_OBJS += builtin/shortlog.o
 BUILTIN_OBJS += builtin/show-branch.o
 BUILTIN_OBJS += builtin/show-index.o
 BUILTIN_OBJS += builtin/show-ref.o
+BUILTIN_OBJS += builtin/stash.o
 BUILTIN_OBJS += builtin/stripspace.o
 BUILTIN_OBJS += builtin/submodule--helper.o
 BUILTIN_OBJS += builtin/symbolic-ref.o
@@ -1198,6 +1198,7 @@ BASIC_CFLAGS += -fsanitize=$(SANITIZE) -fno-sanitize-recover=$(SANITIZE)
 BASIC_CFLAGS += -fno-omit-frame-pointer
 ifneq ($(filter undefined,$(SANITIZERS)),)
 BASIC_CFLAGS += -DNO_UNALIGNED_LOADS
+BASIC_CFLAGS += -DSHA1DC_FORCE_ALIGNED_ACCESS
 endif
 ifneq ($(filter leak,$(SANITIZERS)),)
 BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
@@ -3077,7 +3078,7 @@ ALL_COMMANDS += git-gui git-citool
 .PHONY: check-docs
 check-docs::
        $(MAKE) -C Documentation lint-docs
-       @(for v in $(ALL_COMMANDS); \
+       @(for v in $(patsubst %$X,%,$(ALL_COMMANDS)); \
        do \
                case "$$v" in \
                git-merge-octopus | git-merge-ours | git-merge-recursive | \
@@ -3098,15 +3099,16 @@ check-docs::
        ( \
                sed -e '1,/^### command list/d' \
                    -e '/^#/d' \
+                   -e '/guide$$/d' \
                    -e 's/[     ].*//' \
                    -e 's/^/listed /' command-list.txt; \
                $(MAKE) -C Documentation print-man1 | \
                grep '\.txt$$' | \
-               sed -e 's|Documentation/|documented |' \
+               sed -e 's|^|documented |' \
                    -e 's/\.txt//'; \
        ) | while read how cmd; \
        do \
-               case " $(ALL_COMMANDS) " in \
+               case " $(patsubst %$X,%,$(ALL_COMMANDS)) " in \
                *" $$cmd "*)    ;; \
                *) echo "removed but $$how: $$cmd" ;; \
                esac; \
index 6538932e99a72f1be97c41806ca6e394f18f9a0c..b78ab6e30b26f6a079c2f747c3787ead04b57962 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -225,6 +225,7 @@ extern int cmd_show(int argc, const char **argv, const char *prefix);
 extern int cmd_show_branch(int argc, const char **argv, const char *prefix);
 extern int cmd_show_index(int argc, const char **argv, const char *prefix);
 extern int cmd_status(int argc, const char **argv, const char *prefix);
+extern int cmd_stash(int argc, const char **argv, const char *prefix);
 extern int cmd_stripspace(int argc, const char **argv, const char *prefix);
 extern int cmd_submodule__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_symbolic_ref(int argc, const char **argv, const char *prefix);
index 72f7110fd80a38431734b8996a9b2f1f6c7887a0..2e72a5e5a944e99ef7e3e44cdcb2e8b2feb22a90 100644 (file)
@@ -726,6 +726,8 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        struct tree *result;
                        struct tree *work;
                        struct merge_options o;
+                       struct strbuf sb = STRBUF_INIT;
+
                        if (!opts->merge)
                                return 1;
 
@@ -736,6 +738,13 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        if (!old_branch_info->commit)
                                return 1;
 
+                       if (repo_index_has_changes(the_repository,
+                                                  get_commit_tree(old_branch_info->commit),
+                                                  &sb))
+                               warning(_("staged changes in the following files may be lost: %s"),
+                                       sb.buf);
+                       strbuf_release(&sb);
+
                        /* Do more real merge */
 
                        /*
index 6090217025a27281b0723537e570357bf9c86629..6ca002893f47516ee8963db09d60b3716b60a205 100644 (file)
@@ -156,6 +156,9 @@ static int git_init_db_config(const char *k, const char *v, void *cb)
        if (!strcmp(k, "init.templatedir"))
                return git_config_pathname(&init_db_template_dir, k, v);
 
+       if (starts_with(k, "core."))
+               return platform_core_config(k, v, cb);
+
        return 0;
 }
 
@@ -186,6 +189,7 @@ static int create_default_files(const char *template_path,
        struct strbuf err = STRBUF_INIT;
 
        /* Just look for `init.templatedir` */
+       init_db_template_dir = NULL; /* re-set in case it was set before */
        git_config(git_init_db_config, NULL);
 
        /*
@@ -362,6 +366,9 @@ int init_db(const char *git_dir, const char *real_git_dir,
        }
        startup_info->have_repository = 1;
 
+       /* Just look for `core.hidedotfiles` */
+       git_config(git_init_db_config, NULL);
+
        safe_create_dir(git_dir, 0);
 
        init_is_bare_repository = is_bare_repository();
index 35314d12ec008df5232bc99fb337ecad86476721..e43ee12fb1dd33c669056ae2b02c12ecc0b73d67 100644 (file)
@@ -517,7 +517,7 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
        if (get_oid_with_context(the_repository, obj_name,
                                 GET_OID_RECORD_PATH,
                                 &oidc, &obj_context))
-               die(_("Not a valid object name %s"), obj_name);
+               die(_("not a valid object name %s"), obj_name);
        if (!obj_context.path ||
            !textconv_object(the_repository, obj_context.path,
                             obj_context.mode, &oidc, 1, &buf, &size)) {
@@ -541,7 +541,7 @@ static int show_tag_object(const struct object_id *oid, struct rev_info *rev)
        int offset = 0;
 
        if (!buf)
-               return error(_("Could not read object %s"), oid_to_hex(oid));
+               return error(_("could not read object %s"), oid_to_hex(oid));
 
        assert(type == OBJ_TAG);
        while (offset < size && buf[offset] != '\n') {
@@ -635,7 +635,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                                break;
                        o = parse_object(the_repository, &t->tagged->oid);
                        if (!o)
-                               ret = error(_("Could not read object %s"),
+                               ret = error(_("could not read object %s"),
                                            oid_to_hex(&t->tagged->oid));
                        objects[i].item = o;
                        i--;
@@ -660,7 +660,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                        ret = cmd_log_walk(&rev);
                        break;
                default:
-                       ret = error(_("Unknown type: %d"), o->type);
+                       ret = error(_("unknown type: %d"), o->type);
                }
        }
        free(objects);
@@ -898,7 +898,7 @@ static int open_next_file(struct commit *commit, const char *subject,
                printf("%s\n", filename.buf + outdir_offset);
 
        if ((rev->diffopt.file = fopen(filename.buf, "w")) == NULL) {
-               error_errno(_("Cannot open patch file %s"), filename.buf);
+               error_errno(_("cannot open patch file %s"), filename.buf);
                strbuf_release(&filename);
                return -1;
        }
@@ -915,7 +915,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
        unsigned flags1, flags2;
 
        if (rev->pending.nr != 2)
-               die(_("Need exactly one range."));
+               die(_("need exactly one range"));
 
        o1 = rev->pending.objects[0].item;
        o2 = rev->pending.objects[1].item;
@@ -925,7 +925,7 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
        c2 = lookup_commit_reference(the_repository, &o2->oid);
 
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
-               die(_("Not a range."));
+               die(_("not a range"));
 
        init_patch_ids(the_repository, ids);
 
@@ -1048,13 +1048,13 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        struct commit *head = list[0];
 
        if (!cmit_fmt_is_mail(rev->commit_format))
-               die(_("Cover letter needs email format"));
+               die(_("cover letter needs email format"));
 
        committer = git_committer_info(0);
 
        if (!use_stdout &&
            open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
-               return;
+               die(_("failed to create cover-letter file"));
 
        log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
 
@@ -1218,7 +1218,7 @@ static int output_directory_callback(const struct option *opt, const char *arg,
        const char **dir = (const char **)opt->value;
        BUG_ON_OPT_NEG(unset);
        if (*dir)
-               die(_("Two output directories?"));
+               die(_("two output directories?"));
        *dir = arg;
        return 0;
 }
@@ -1329,7 +1329,7 @@ static struct commit *get_base_commit(const char *base_commit,
        if (base_commit && strcmp(base_commit, "auto")) {
                base = lookup_commit_reference_by_name(base_commit);
                if (!base)
-                       die(_("Unknown commit %s"), base_commit);
+                       die(_("unknown commit %s"), base_commit);
        } else if ((base_commit && !strcmp(base_commit, "auto")) || base_auto) {
                struct branch *curr_branch = branch_get(NULL);
                const char *upstream = branch_get_upstream(curr_branch, NULL);
@@ -1339,18 +1339,18 @@ static struct commit *get_base_commit(const char *base_commit,
                        struct object_id oid;
 
                        if (get_oid(upstream, &oid))
-                               die(_("Failed to resolve '%s' as a valid ref."), upstream);
+                               die(_("failed to resolve '%s' as a valid ref"), upstream);
                        commit = lookup_commit_or_die(&oid, "upstream base");
                        base_list = get_merge_bases_many(commit, total, list);
                        /* There should be one and only one merge base. */
                        if (!base_list || base_list->next)
-                               die(_("Could not find exact merge base."));
+                               die(_("could not find exact merge base"));
                        base = base_list->item;
                        free_commit_list(base_list);
                } else {
-                       die(_("Failed to get upstream, if you want to record base commit automatically,\n"
+                       die(_("failed to get upstream, if you want to record base commit automatically,\n"
                              "please use git branch --set-upstream-to to track a remote branch.\n"
-                             "Or you could specify base commit by --base=<base-commit-id> manually."));
+                             "Or you could specify base commit by --base=<base-commit-id> manually"));
                }
        }
 
@@ -1368,7 +1368,7 @@ static struct commit *get_base_commit(const char *base_commit,
                        struct commit_list *merge_base;
                        merge_base = get_merge_bases(rev[2 * i], rev[2 * i + 1]);
                        if (!merge_base || merge_base->next)
-                               die(_("Failed to find exact merge base"));
+                               die(_("failed to find exact merge base"));
 
                        rev[i] = merge_base->item;
                }
@@ -1747,7 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                if (use_stdout)
                        die(_("standard output, or directory, which one?"));
                if (mkdir(output_directory, 0777) < 0 && errno != EEXIST)
-                       die_errno(_("Could not create directory '%s'"),
+                       die_errno(_("could not create directory '%s'"),
                                  output_directory);
        }
 
@@ -1949,7 +1949,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 
                if (!use_stdout &&
                    open_next_file(rev.numbered_files ? NULL : commit, NULL, &rev, quiet))
-                       die(_("Failed to create output files"));
+                       die(_("failed to create output files"));
                shown = log_tree_commit(&rev, commit);
                free_commit_buffer(the_repository->parsed_objects,
                                   commit);
@@ -2073,9 +2073,9 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
        revs.max_parents = 1;
 
        if (add_pending_commit(head, &revs, 0))
-               die(_("Unknown commit %s"), head);
+               die(_("unknown commit %s"), head);
        if (add_pending_commit(upstream, &revs, UNINTERESTING))
-               die(_("Unknown commit %s"), upstream);
+               die(_("unknown commit %s"), upstream);
 
        /* Don't say anything if head and upstream are the same. */
        if (revs.pending.nr == 2) {
@@ -2087,7 +2087,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
        get_patch_ids(&revs, &ids);
 
        if (limit && add_pending_commit(limit, &revs, UNINTERESTING))
-               die(_("Unknown commit %s"), limit);
+               die(_("unknown commit %s"), limit);
 
        /* reverse the list of commits */
        if (prepare_revision_walk(&revs))
index fca70f8e4fcca8432619c41fd68234732a92f07a..ae6e476ad567e57e5a1e308d7901b0a0c331640f 100644 (file)
@@ -3,6 +3,7 @@
 #include "config.h"
 #include "parse-options.h"
 #include "midx.h"
+#include "trace2.h"
 
 static char const * const builtin_multi_pack_index_usage[] = {
        N_("git multi-pack-index [--object-dir=<dir>] (write|verify)"),
@@ -40,6 +41,8 @@ int cmd_multi_pack_index(int argc, const char **argv,
                return 1;
        }
 
+       trace2_cmd_mode(argv[0]);
+
        if (!strcmp(argv[0], "write"))
                return write_midx_file(opts.object_dir);
        if (!strcmp(argv[0], "verify"))
index 888390f9114321a489f8210659487ef2d2897fae..4535523bf53f989759176c550d20d75b5ef7e587 100644 (file)
@@ -14,6 +14,103 @@ static GIT_PATH_FUNC(path_state_dir, "rebase-merge/")
 static GIT_PATH_FUNC(path_squash_onto, "rebase-merge/squash-onto")
 static GIT_PATH_FUNC(path_interactive, "rebase-merge/interactive")
 
+static int add_exec_commands(struct string_list *commands)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       int res;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                       &todo_list)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       todo_list_add_exec_commands(&todo_list, commands);
+       res = todo_list_write_to_file(the_repository, &todo_list,
+                                     todo_file, NULL, NULL, -1, 0);
+       todo_list_release(&todo_list);
+
+       if (res)
+               return error_errno(_("could not write '%s'."), todo_file);
+       return 0;
+}
+
+static int rearrange_squash_in_todo_file(void)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       int res = 0;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                       &todo_list)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       res = todo_list_rearrange_squash(&todo_list);
+       if (!res)
+               res = todo_list_write_to_file(the_repository, &todo_list,
+                                             todo_file, NULL, NULL, -1, 0);
+
+       todo_list_release(&todo_list);
+
+       if (res)
+               return error_errno(_("could not write '%s'."), todo_file);
+       return 0;
+}
+
+static int transform_todo_file(unsigned flags)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT;
+       int res;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                       &todo_list)) {
+               todo_list_release(&todo_list);
+               return error(_("unusable todo list: '%s'"), todo_file);
+       }
+
+       res = todo_list_write_to_file(the_repository, &todo_list, todo_file,
+                                     NULL, NULL, -1, flags);
+       todo_list_release(&todo_list);
+
+       if (res)
+               return error_errno(_("could not write '%s'."), todo_file);
+       return 0;
+}
+
+static int edit_todo_file(unsigned flags)
+{
+       const char *todo_file = rebase_path_todo();
+       struct todo_list todo_list = TODO_LIST_INIT,
+               new_todo = TODO_LIST_INIT;
+       int res = 0;
+
+       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
+               return error_errno(_("could not read '%s'."), todo_file);
+
+       strbuf_stripspace(&todo_list.buf, 1);
+       res = edit_todo_list(the_repository, &todo_list, &new_todo, NULL, NULL, flags);
+       if (!res && todo_list_write_to_file(the_repository, &new_todo, todo_file,
+                                           NULL, NULL, -1, flags & ~(TODO_LIST_SHORTEN_IDS)))
+               res = error_errno(_("could not write '%s'"), todo_file);
+
+       todo_list_release(&todo_list);
+       todo_list_release(&new_todo);
+
+       return res;
+}
+
 static int get_revision_ranges(const char *upstream, const char *onto,
                               const char **head_hash,
                               char **revisions, char **shortrevisions)
@@ -66,13 +163,13 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
                                 const char *onto, const char *onto_name,
                                 const char *squash_onto, const char *head_name,
                                 const char *restrict_revision, char *raw_strategies,
-                                const char *cmd, unsigned autosquash)
+                                struct string_list *commands, unsigned autosquash)
 {
        int ret;
        const char *head_hash = NULL;
        char *revisions = NULL, *shortrevisions = NULL;
        struct argv_array make_script_args = ARGV_ARRAY_INIT;
-       FILE *todo_list;
+       struct todo_list todo_list = TODO_LIST_INIT;
 
        if (prepare_branch_to_be_rebased(opts, switch_to))
                return -1;
@@ -94,34 +191,29 @@ static int do_interactive_rebase(struct replay_opts *opts, unsigned flags,
        if (!upstream && squash_onto)
                write_file(path_squash_onto(), "%s\n", squash_onto);
 
-       todo_list = fopen(rebase_path_todo(), "w");
-       if (!todo_list) {
-               free(revisions);
-               free(shortrevisions);
-
-               return error_errno(_("could not open %s"), rebase_path_todo());
-       }
-
        argv_array_pushl(&make_script_args, "", revisions, NULL);
        if (restrict_revision)
                argv_array_push(&make_script_args, restrict_revision);
 
-       ret = sequencer_make_script(the_repository, todo_list,
+       ret = sequencer_make_script(the_repository, &todo_list.buf,
                                    make_script_args.argc, make_script_args.argv,
                                    flags);
-       fclose(todo_list);
 
        if (ret)
                error(_("could not generate todo list"));
        else {
                discard_cache();
-               ret = complete_action(the_repository, opts, flags,
-                                     shortrevisions, onto_name, onto,
-                                     head_hash, cmd, autosquash);
+               if (todo_list_parse_insn_buffer(the_repository, todo_list.buf.buf,
+                                               &todo_list))
+                       BUG("unusable todo list");
+
+               ret = complete_action(the_repository, opts, flags, shortrevisions, onto_name,
+                                     onto, head_hash, commands, autosquash, &todo_list);
        }
 
        free(revisions);
        free(shortrevisions);
+       todo_list_release(&todo_list);
        argv_array_clear(&make_script_args);
 
        return ret;
@@ -140,6 +232,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        const char *onto = NULL, *onto_name = NULL, *restrict_revision = NULL,
                *squash_onto = NULL, *upstream = NULL, *head_name = NULL,
                *switch_to = NULL, *cmd = NULL;
+       struct string_list commands = STRING_LIST_INIT_DUP;
        char *raw_strategies = NULL;
        enum {
                NONE = 0, CONTINUE, SKIP, EDIT_TODO, SHOW_CURRENT_PATCH,
@@ -224,6 +317,14 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
                warning(_("--[no-]rebase-cousins has no effect without "
                          "--rebase-merges"));
 
+       if (cmd && *cmd) {
+               string_list_split(&commands, cmd, '\n', -1);
+
+               /* rebase.c adds a new line to cmd after every command,
+                * so here the last command is always empty */
+               string_list_remove_empty_items(&commands, 0);
+       }
+
        switch (command) {
        case NONE:
                if (!onto && !upstream)
@@ -231,7 +332,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
 
                ret = do_interactive_rebase(&opts, flags, switch_to, upstream, onto,
                                            onto_name, squash_onto, head_name, restrict_revision,
-                                           raw_strategies, cmd, autosquash);
+                                           raw_strategies, &commands, autosquash);
                break;
        case SKIP: {
                struct string_list merge_rr = STRING_LIST_INIT_DUP;
@@ -243,7 +344,7 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
                break;
        }
        case EDIT_TODO:
-               ret = edit_todo_list(the_repository, flags);
+               ret = edit_todo_file(flags);
                break;
        case SHOW_CURRENT_PATCH: {
                struct child_process cmd = CHILD_PROCESS_INIT;
@@ -256,20 +357,21 @@ int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)
        }
        case SHORTEN_OIDS:
        case EXPAND_OIDS:
-               ret = transform_todos(the_repository, flags);
+               ret = transform_todo_file(flags);
                break;
        case CHECK_TODO_LIST:
-               ret = check_todo_list(the_repository);
+               ret = check_todo_list_from_file(the_repository);
                break;
        case REARRANGE_SQUASH:
-               ret = rearrange_squash(the_repository);
+               ret = rearrange_squash_in_todo_file();
                break;
        case ADD_EXEC:
-               ret = sequencer_add_exec_commands(the_repository, cmd);
+               ret = add_exec_commands(&commands);
                break;
        default:
                BUG("invalid command '%d'", command);
        }
 
+       string_list_clear(&commands, 0);
        return !!ret;
 }
index 77deebc65c6bd7fbe6261b91cfb78c7ec3f85c2d..2e41ad5644c11db0d496aa3c8374cc194b331e78 100644 (file)
@@ -46,29 +46,6 @@ enum rebase_type {
        REBASE_PRESERVE_MERGES
 };
 
-static int use_builtin_rebase(void)
-{
-       struct child_process cp = CHILD_PROCESS_INIT;
-       struct strbuf out = STRBUF_INIT;
-       int ret, env = git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1);
-
-       if (env != -1)
-               return env;
-
-       argv_array_pushl(&cp.args,
-                        "config", "--bool", "rebase.usebuiltin", NULL);
-       cp.git_cmd = 1;
-       if (capture_command(&cp, &out, 6)) {
-               strbuf_release(&out);
-               return 1;
-       }
-
-       strbuf_trim(&out);
-       ret = !strcmp("true", out.buf);
-       strbuf_release(&out);
-       return ret;
-}
-
 struct rebase_options {
        enum rebase_type type;
        const char *state_dir;
@@ -106,6 +83,7 @@ struct rebase_options {
        char *strategy, *strategy_opts;
        struct strbuf git_format_patch_opt;
        int reschedule_failed_exec;
+       int use_legacy_rebase;
 };
 
 static int is_interactive(struct rebase_options *opts)
@@ -874,6 +852,11 @@ static int rebase_config(const char *var, const char *value, void *data)
                return 0;
        }
 
+       if (!strcmp(var, "rebase.usebuiltin")) {
+               opts->use_legacy_rebase = !git_config_bool(var, value);
+               return 0;
+       }
+
        return git_default_config(var, value, data);
 }
 
@@ -1105,8 +1088,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        PARSE_OPT_NOARG | PARSE_OPT_NONEG,
                        parse_opt_interactive },
                OPT_SET_INT('p', "preserve-merges", &options.type,
-                           N_("try to recreate merges instead of ignoring "
-                              "them"), REBASE_PRESERVE_MERGES),
+                           N_("(DEPRECATED) try to recreate merges instead of "
+                              "ignoring them"), REBASE_PRESERVE_MERGES),
                OPT_BOOL(0, "rerere-autoupdate",
                         &options.allow_rerere_autoupdate,
                         N_("allow rerere to update index with resolved "
@@ -1148,22 +1131,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
        };
        int i;
 
-       /*
-        * NEEDSWORK: Once the builtin rebase has been tested enough
-        * and git-legacy-rebase.sh is retired to contrib/, this preamble
-        * can be removed.
-        */
-
-       if (!use_builtin_rebase()) {
-               const char *path = mkpath("%s/git-legacy-rebase",
-                                         git_exec_path());
-
-               if (sane_execvp(path, (char **)argv) < 0)
-                       die_errno(_("could not exec %s"), path);
-               else
-                       BUG("sane_execvp() returned???");
-       }
-
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
@@ -1174,6 +1141,11 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
 
        git_config(rebase_config, &options);
 
+       if (options.use_legacy_rebase ||
+           !git_env_bool("GIT_TEST_REBASE_USE_BUILTIN", -1))
+               warning(_("the rebase.useBuiltin support has been removed!\n"
+                         "See its entry in 'git help config' for details."));
+
        strbuf_reset(&buf);
        strbuf_addf(&buf, "%s/applying", apply_dir());
        if(file_exists(buf.buf))
@@ -1217,6 +1189,10 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                usage_with_options(builtin_rebase_usage,
                                   builtin_rebase_options);
 
+       if (options.type == REBASE_PRESERVE_MERGES)
+               warning(_("git rebase --preserve-merges is deprecated. "
+                         "Use --rebase-merges instead."));
+
        if (action != NO_ACTION && !in_progress)
                die(_("No rebase in progress?"));
        setenv(GIT_REFLOG_ACTION_ENVIRONMENT, "rebase", 0);
index 7882829a95d8294ea5bb5bcba3335e651233d79a..26ef9a7bd03ac8925e1cb350c49e5e327999ba52 100644 (file)
@@ -386,6 +386,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
                        if (read_from_tree(&pathspec, &oid, intent_to_add))
                                return 1;
+                       the_index.updated_skipworktree = 1;
                        if (!quiet && get_git_work_tree()) {
                                uint64_t t_begin, t_delta_in_ms;
 
diff --git a/builtin/stash.c b/builtin/stash.c
new file mode 100644 (file)
index 0000000..2a8e6d0
--- /dev/null
@@ -0,0 +1,1648 @@
+#define USE_THE_INDEX_COMPATIBILITY_MACROS
+#include "builtin.h"
+#include "config.h"
+#include "parse-options.h"
+#include "refs.h"
+#include "lockfile.h"
+#include "cache-tree.h"
+#include "unpack-trees.h"
+#include "merge-recursive.h"
+#include "argv-array.h"
+#include "run-command.h"
+#include "dir.h"
+#include "rerere.h"
+#include "revision.h"
+#include "log-tree.h"
+#include "diffcore.h"
+#include "exec-cmd.h"
+
+#define INCLUDE_ALL_FILES 2
+
+static const char * const git_stash_usage[] = {
+       N_("git stash list [<options>]"),
+       N_("git stash show [<options>] [<stash>]"),
+       N_("git stash drop [-q|--quiet] [<stash>]"),
+       N_("git stash ( pop | apply ) [--index] [-q|--quiet] [<stash>]"),
+       N_("git stash branch <branchname> [<stash>]"),
+       N_("git stash clear"),
+       N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+          "          [--] [<pathspec>...]]"),
+       N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [<message>]"),
+       NULL
+};
+
+static const char * const git_stash_list_usage[] = {
+       N_("git stash list [<options>]"),
+       NULL
+};
+
+static const char * const git_stash_show_usage[] = {
+       N_("git stash show [<options>] [<stash>]"),
+       NULL
+};
+
+static const char * const git_stash_drop_usage[] = {
+       N_("git stash drop [-q|--quiet] [<stash>]"),
+       NULL
+};
+
+static const char * const git_stash_pop_usage[] = {
+       N_("git stash pop [--index] [-q|--quiet] [<stash>]"),
+       NULL
+};
+
+static const char * const git_stash_apply_usage[] = {
+       N_("git stash apply [--index] [-q|--quiet] [<stash>]"),
+       NULL
+};
+
+static const char * const git_stash_branch_usage[] = {
+       N_("git stash branch <branchname> [<stash>]"),
+       NULL
+};
+
+static const char * const git_stash_clear_usage[] = {
+       N_("git stash clear"),
+       NULL
+};
+
+static const char * const git_stash_store_usage[] = {
+       N_("git stash store [-m|--message <message>] [-q|--quiet] <commit>"),
+       NULL
+};
+
+static const char * const git_stash_push_usage[] = {
+       N_("git stash [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [-m|--message <message>]\n"
+          "          [--] [<pathspec>...]]"),
+       NULL
+};
+
+static const char * const git_stash_save_usage[] = {
+       N_("git stash save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]\n"
+          "          [-u|--include-untracked] [-a|--all] [<message>]"),
+       NULL
+};
+
+static const char *ref_stash = "refs/stash";
+static struct strbuf stash_index_path = STRBUF_INIT;
+
+/*
+ * w_commit is set to the commit containing the working tree
+ * b_commit is set to the base commit
+ * i_commit is set to the commit containing the index tree
+ * u_commit is set to the commit containing the untracked files tree
+ * w_tree is set to the working tree
+ * b_tree is set to the base tree
+ * i_tree is set to the index tree
+ * u_tree is set to the untracked files tree
+ */
+struct stash_info {
+       struct object_id w_commit;
+       struct object_id b_commit;
+       struct object_id i_commit;
+       struct object_id u_commit;
+       struct object_id w_tree;
+       struct object_id b_tree;
+       struct object_id i_tree;
+       struct object_id u_tree;
+       struct strbuf revision;
+       int is_stash_ref;
+       int has_u;
+};
+
+static void free_stash_info(struct stash_info *info)
+{
+       strbuf_release(&info->revision);
+}
+
+static void assert_stash_like(struct stash_info *info, const char *revision)
+{
+       if (get_oidf(&info->b_commit, "%s^1", revision) ||
+           get_oidf(&info->w_tree, "%s:", revision) ||
+           get_oidf(&info->b_tree, "%s^1:", revision) ||
+           get_oidf(&info->i_tree, "%s^2:", revision))
+               die(_("'%s' is not a stash-like commit"), revision);
+}
+
+static int get_stash_info(struct stash_info *info, int argc, const char **argv)
+{
+       int ret;
+       char *end_of_rev;
+       char *expanded_ref;
+       const char *revision;
+       const char *commit = NULL;
+       struct object_id dummy;
+       struct strbuf symbolic = STRBUF_INIT;
+
+       if (argc > 1) {
+               int i;
+               struct strbuf refs_msg = STRBUF_INIT;
+
+               for (i = 0; i < argc; i++)
+                       strbuf_addf(&refs_msg, " '%s'", argv[i]);
+
+               fprintf_ln(stderr, _("Too many revisions specified:%s"),
+                          refs_msg.buf);
+               strbuf_release(&refs_msg);
+
+               return -1;
+       }
+
+       if (argc == 1)
+               commit = argv[0];
+
+       strbuf_init(&info->revision, 0);
+       if (!commit) {
+               if (!ref_exists(ref_stash)) {
+                       free_stash_info(info);
+                       fprintf_ln(stderr, _("No stash entries found."));
+                       return -1;
+               }
+
+               strbuf_addf(&info->revision, "%s@{0}", ref_stash);
+       } else if (strspn(commit, "0123456789") == strlen(commit)) {
+               strbuf_addf(&info->revision, "%s@{%s}", ref_stash, commit);
+       } else {
+               strbuf_addstr(&info->revision, commit);
+       }
+
+       revision = info->revision.buf;
+
+       if (get_oid(revision, &info->w_commit)) {
+               error(_("%s is not a valid reference"), revision);
+               free_stash_info(info);
+               return -1;
+       }
+
+       assert_stash_like(info, revision);
+
+       info->has_u = !get_oidf(&info->u_tree, "%s^3:", revision);
+
+       end_of_rev = strchrnul(revision, '@');
+       strbuf_add(&symbolic, revision, end_of_rev - revision);
+
+       ret = dwim_ref(symbolic.buf, symbolic.len, &dummy, &expanded_ref);
+       strbuf_release(&symbolic);
+       switch (ret) {
+       case 0: /* Not found, but valid ref */
+               info->is_stash_ref = 0;
+               break;
+       case 1:
+               info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
+               break;
+       default: /* Invalid or ambiguous */
+               free_stash_info(info);
+       }
+
+       free(expanded_ref);
+       return !(ret == 0 || ret == 1);
+}
+
+static int do_clear_stash(void)
+{
+       struct object_id obj;
+       if (get_oid(ref_stash, &obj))
+               return 0;
+
+       return delete_ref(NULL, ref_stash, &obj, 0);
+}
+
+static int clear_stash(int argc, const char **argv, const char *prefix)
+{
+       struct option options[] = {
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_clear_usage,
+                            PARSE_OPT_STOP_AT_NON_OPTION);
+
+       if (argc)
+               return error(_("git stash clear with parameters is "
+                              "unimplemented"));
+
+       return do_clear_stash();
+}
+
+static int reset_tree(struct object_id *i_tree, int update, int reset)
+{
+       int nr_trees = 1;
+       struct unpack_trees_options opts;
+       struct tree_desc t[MAX_UNPACK_TREES];
+       struct tree *tree;
+       struct lock_file lock_file = LOCK_INIT;
+
+       read_cache_preload(NULL);
+       if (refresh_cache(REFRESH_QUIET))
+               return -1;
+
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+
+       memset(&opts, 0, sizeof(opts));
+
+       tree = parse_tree_indirect(i_tree);
+       if (parse_tree(tree))
+               return -1;
+
+       init_tree_desc(t, tree->buffer, tree->size);
+
+       opts.head_idx = 1;
+       opts.src_index = &the_index;
+       opts.dst_index = &the_index;
+       opts.merge = 1;
+       opts.reset = reset;
+       opts.update = update;
+       opts.fn = oneway_merge;
+
+       if (unpack_trees(nr_trees, t, &opts))
+               return -1;
+
+       if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+               return error(_("unable to write new index file"));
+
+       return 0;
+}
+
+static int diff_tree_binary(struct strbuf *out, struct object_id *w_commit)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const char *w_commit_hex = oid_to_hex(w_commit);
+
+       /*
+        * Diff-tree would not be very hard to replace with a native function,
+        * however it should be done together with apply_cached.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "diff-tree", "--binary", NULL);
+       argv_array_pushf(&cp.args, "%s^2^..%s^2", w_commit_hex, w_commit_hex);
+
+       return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+}
+
+static int apply_cached(struct strbuf *out)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       /*
+        * Apply currently only reads either from stdin or a file, thus
+        * apply_all_patches would have to be updated to optionally take a
+        * buffer.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "apply", "--cached", NULL);
+       return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+}
+
+static int reset_head(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       /*
+        * Reset is overall quite simple, however there is no current public
+        * API for resetting.
+        */
+       cp.git_cmd = 1;
+       argv_array_push(&cp.args, "reset");
+
+       return run_command(&cp);
+}
+
+static void add_diff_to_buf(struct diff_queue_struct *q,
+                           struct diff_options *options,
+                           void *data)
+{
+       int i;
+
+       for (i = 0; i < q->nr; i++) {
+               strbuf_addstr(data, q->queue[i]->one->path);
+
+               /* NUL-terminate: will be fed to update-index -z */
+               strbuf_addch(data, '\0');
+       }
+}
+
+static int get_newly_staged(struct strbuf *out, struct object_id *c_tree)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const char *c_tree_hex = oid_to_hex(c_tree);
+
+       /*
+        * diff-index is very similar to diff-tree above, and should be
+        * converted together with update_index.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "diff-index", "--cached", "--name-only",
+                        "--diff-filter=A", NULL);
+       argv_array_push(&cp.args, c_tree_hex);
+       return pipe_command(&cp, NULL, 0, out, 0, NULL, 0);
+}
+
+static int update_index(struct strbuf *out)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       /*
+        * Update-index is very complicated and may need to have a public
+        * function exposed in order to remove this forking.
+        */
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "update-index", "--add", "--stdin", NULL);
+       return pipe_command(&cp, out->buf, out->len, NULL, 0, NULL, 0);
+}
+
+static int restore_untracked(struct object_id *u_tree)
+{
+       int res;
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       /*
+        * We need to run restore files from a given index, but without
+        * affecting the current index, so we use GIT_INDEX_FILE with
+        * run_command to fork processes that will not interfere.
+        */
+       cp.git_cmd = 1;
+       argv_array_push(&cp.args, "read-tree");
+       argv_array_push(&cp.args, oid_to_hex(u_tree));
+       argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       if (run_command(&cp)) {
+               remove_path(stash_index_path.buf);
+               return -1;
+       }
+
+       child_process_init(&cp);
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "checkout-index", "--all", NULL);
+       argv_array_pushf(&cp.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+
+       res = run_command(&cp);
+       remove_path(stash_index_path.buf);
+       return res;
+}
+
+static int do_apply_stash(const char *prefix, struct stash_info *info,
+                         int index, int quiet)
+{
+       int ret;
+       int has_index = index;
+       struct merge_options o;
+       struct object_id c_tree;
+       struct object_id index_tree;
+       struct commit *result;
+       const struct object_id *bases[1];
+
+       read_cache_preload(NULL);
+       if (refresh_cache(REFRESH_QUIET))
+               return -1;
+
+       if (write_cache_as_tree(&c_tree, 0, NULL))
+               return error(_("cannot apply a stash in the middle of a merge"));
+
+       if (index) {
+               if (oideq(&info->b_tree, &info->i_tree) ||
+                   oideq(&c_tree, &info->i_tree)) {
+                       has_index = 0;
+               } else {
+                       struct strbuf out = STRBUF_INIT;
+
+                       if (diff_tree_binary(&out, &info->w_commit)) {
+                               strbuf_release(&out);
+                               return error(_("could not generate diff %s^!."),
+                                            oid_to_hex(&info->w_commit));
+                       }
+
+                       ret = apply_cached(&out);
+                       strbuf_release(&out);
+                       if (ret)
+                               return error(_("conflicts in index."
+                                              "Try without --index."));
+
+                       discard_cache();
+                       read_cache();
+                       if (write_cache_as_tree(&index_tree, 0, NULL))
+                               return error(_("could not save index tree"));
+
+                       reset_head();
+               }
+       }
+
+       if (info->has_u && restore_untracked(&info->u_tree))
+               return error(_("could not restore untracked files from stash"));
+
+       init_merge_options(&o, the_repository);
+
+       o.branch1 = "Updated upstream";
+       o.branch2 = "Stashed changes";
+
+       if (oideq(&info->b_tree, &c_tree))
+               o.branch1 = "Version stash was based on";
+
+       if (quiet)
+               o.verbosity = 0;
+
+       if (o.verbosity >= 3)
+               printf_ln(_("Merging %s with %s"), o.branch1, o.branch2);
+
+       bases[0] = &info->b_tree;
+
+       ret = merge_recursive_generic(&o, &c_tree, &info->w_tree, 1, bases,
+                                     &result);
+       if (ret) {
+               rerere(0);
+
+               if (index)
+                       fprintf_ln(stderr, _("Index was not unstashed."));
+
+               return ret;
+       }
+
+       if (has_index) {
+               if (reset_tree(&index_tree, 0, 0))
+                       return -1;
+       } else {
+               struct strbuf out = STRBUF_INIT;
+
+               if (get_newly_staged(&out, &c_tree)) {
+                       strbuf_release(&out);
+                       return -1;
+               }
+
+               if (reset_tree(&c_tree, 0, 1)) {
+                       strbuf_release(&out);
+                       return -1;
+               }
+
+               ret = update_index(&out);
+               strbuf_release(&out);
+               if (ret)
+                       return -1;
+
+               discard_cache();
+       }
+
+       if (quiet) {
+               if (refresh_cache(REFRESH_QUIET))
+                       warning("could not refresh index");
+       } else {
+               struct child_process cp = CHILD_PROCESS_INIT;
+
+               /*
+                * Status is quite simple and could be replaced with calls to
+                * wt_status in the future, but it adds complexities which may
+                * require more tests.
+                */
+               cp.git_cmd = 1;
+               cp.dir = prefix;
+               argv_array_push(&cp.args, "status");
+               run_command(&cp);
+       }
+
+       return 0;
+}
+
+static int apply_stash(int argc, const char **argv, const char *prefix)
+{
+       int ret;
+       int quiet = 0;
+       int index = 0;
+       struct stash_info info;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+               OPT_BOOL(0, "index", &index,
+                        N_("attempt to recreate the index")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_apply_usage, 0);
+
+       if (get_stash_info(&info, argc, argv))
+               return -1;
+
+       ret = do_apply_stash(prefix, &info, index, quiet);
+       free_stash_info(&info);
+       return ret;
+}
+
+static int do_drop_stash(struct stash_info *info, int quiet)
+{
+       int ret;
+       struct child_process cp_reflog = CHILD_PROCESS_INIT;
+       struct child_process cp = CHILD_PROCESS_INIT;
+
+       /*
+        * reflog does not provide a simple function for deleting refs. One will
+        * need to be added to avoid implementing too much reflog code here
+        */
+
+       cp_reflog.git_cmd = 1;
+       argv_array_pushl(&cp_reflog.args, "reflog", "delete", "--updateref",
+                        "--rewrite", NULL);
+       argv_array_push(&cp_reflog.args, info->revision.buf);
+       ret = run_command(&cp_reflog);
+       if (!ret) {
+               if (!quiet)
+                       printf_ln(_("Dropped %s (%s)"), info->revision.buf,
+                                 oid_to_hex(&info->w_commit));
+       } else {
+               return error(_("%s: Could not drop stash entry"),
+                            info->revision.buf);
+       }
+
+       /*
+        * This could easily be replaced by get_oid, but currently it will throw
+        * a fatal error when a reflog is empty, which we can not recover from.
+        */
+       cp.git_cmd = 1;
+       /* Even though --quiet is specified, rev-parse still outputs the hash */
+       cp.no_stdout = 1;
+       argv_array_pushl(&cp.args, "rev-parse", "--verify", "--quiet", NULL);
+       argv_array_pushf(&cp.args, "%s@{0}", ref_stash);
+       ret = run_command(&cp);
+
+       /* do_clear_stash if we just dropped the last stash entry */
+       if (ret)
+               do_clear_stash();
+
+       return 0;
+}
+
+static void assert_stash_ref(struct stash_info *info)
+{
+       if (!info->is_stash_ref) {
+               error(_("'%s' is not a stash reference"), info->revision.buf);
+               free_stash_info(info);
+               exit(1);
+       }
+}
+
+static int drop_stash(int argc, const char **argv, const char *prefix)
+{
+       int ret;
+       int quiet = 0;
+       struct stash_info info;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_drop_usage, 0);
+
+       if (get_stash_info(&info, argc, argv))
+               return -1;
+
+       assert_stash_ref(&info);
+
+       ret = do_drop_stash(&info, quiet);
+       free_stash_info(&info);
+       return ret;
+}
+
+static int pop_stash(int argc, const char **argv, const char *prefix)
+{
+       int ret;
+       int index = 0;
+       int quiet = 0;
+       struct stash_info info;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet, only report errors")),
+               OPT_BOOL(0, "index", &index,
+                        N_("attempt to recreate the index")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_pop_usage, 0);
+
+       if (get_stash_info(&info, argc, argv))
+               return -1;
+
+       assert_stash_ref(&info);
+       if ((ret = do_apply_stash(prefix, &info, index, quiet)))
+               printf_ln(_("The stash entry is kept in case "
+                           "you need it again."));
+       else
+               ret = do_drop_stash(&info, quiet);
+
+       free_stash_info(&info);
+       return ret;
+}
+
+static int branch_stash(int argc, const char **argv, const char *prefix)
+{
+       int ret;
+       const char *branch = NULL;
+       struct stash_info info;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct option options[] = {
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_branch_usage, 0);
+
+       if (!argc) {
+               fprintf_ln(stderr, _("No branch name specified"));
+               return -1;
+       }
+
+       branch = argv[0];
+
+       if (get_stash_info(&info, argc - 1, argv + 1))
+               return -1;
+
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "checkout", "-b", NULL);
+       argv_array_push(&cp.args, branch);
+       argv_array_push(&cp.args, oid_to_hex(&info.b_commit));
+       ret = run_command(&cp);
+       if (!ret)
+               ret = do_apply_stash(prefix, &info, 1, 0);
+       if (!ret && info.is_stash_ref)
+               ret = do_drop_stash(&info, 0);
+
+       free_stash_info(&info);
+
+       return ret;
+}
+
+static int list_stash(int argc, const char **argv, const char *prefix)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct option options[] = {
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_list_usage,
+                            PARSE_OPT_KEEP_UNKNOWN);
+
+       if (!ref_exists(ref_stash))
+               return 0;
+
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "log", "--format=%gd: %gs", "-g",
+                        "--first-parent", "-m", NULL);
+       argv_array_pushv(&cp.args, argv);
+       argv_array_push(&cp.args, ref_stash);
+       argv_array_push(&cp.args, "--");
+       return run_command(&cp);
+}
+
+static int show_stat = 1;
+static int show_patch;
+
+static int git_stash_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "stash.showstat")) {
+               show_stat = git_config_bool(var, value);
+               return 0;
+       }
+       if (!strcmp(var, "stash.showpatch")) {
+               show_patch = git_config_bool(var, value);
+               return 0;
+       }
+       return git_default_config(var, value, cb);
+}
+
+static int show_stash(int argc, const char **argv, const char *prefix)
+{
+       int i;
+       int opts = 0;
+       int ret = 0;
+       struct stash_info info;
+       struct rev_info rev;
+       struct argv_array stash_args = ARGV_ARRAY_INIT;
+       struct option options[] = {
+               OPT_END()
+       };
+
+       init_diff_ui_defaults();
+       git_config(git_diff_ui_config, NULL);
+       init_revisions(&rev, prefix);
+
+       for (i = 1; i < argc; i++) {
+               if (argv[i][0] != '-')
+                       argv_array_push(&stash_args, argv[i]);
+               else
+                       opts++;
+       }
+
+       ret = get_stash_info(&info, stash_args.argc, stash_args.argv);
+       argv_array_clear(&stash_args);
+       if (ret)
+               return -1;
+
+       /*
+        * The config settings are applied only if there are not passed
+        * any options.
+        */
+       if (!opts) {
+               git_config(git_stash_config, NULL);
+               if (show_stat)
+                       rev.diffopt.output_format = DIFF_FORMAT_DIFFSTAT;
+
+               if (show_patch)
+                       rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
+
+               if (!show_stat && !show_patch) {
+                       free_stash_info(&info);
+                       return 0;
+               }
+       }
+
+       argc = setup_revisions(argc, argv, &rev, NULL);
+       if (argc > 1) {
+               free_stash_info(&info);
+               usage_with_options(git_stash_show_usage, options);
+       }
+       if (!rev.diffopt.output_format) {
+               rev.diffopt.output_format = DIFF_FORMAT_PATCH;
+               diff_setup_done(&rev.diffopt);
+       }
+
+       rev.diffopt.flags.recursive = 1;
+       setup_diff_pager(&rev.diffopt);
+       diff_tree_oid(&info.b_commit, &info.w_commit, "", &rev.diffopt);
+       log_tree_diff_flush(&rev);
+
+       free_stash_info(&info);
+       return diff_result_code(&rev.diffopt, 0);
+}
+
+static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
+                         int quiet)
+{
+       if (!stash_msg)
+               stash_msg = "Created via \"git stash store\".";
+
+       if (update_ref(stash_msg, ref_stash, w_commit, NULL,
+                      REF_FORCE_CREATE_REFLOG,
+                      quiet ? UPDATE_REFS_QUIET_ON_ERR :
+                      UPDATE_REFS_MSG_ON_ERR)) {
+               if (!quiet) {
+                       fprintf_ln(stderr, _("Cannot update %s with %s"),
+                                  ref_stash, oid_to_hex(w_commit));
+               }
+               return -1;
+       }
+
+       return 0;
+}
+
+static int store_stash(int argc, const char **argv, const char *prefix)
+{
+       int quiet = 0;
+       const char *stash_msg = NULL;
+       struct object_id obj;
+       struct object_context dummy;
+       struct option options[] = {
+               OPT__QUIET(&quiet, N_("be quiet")),
+               OPT_STRING('m', "message", &stash_msg, "message",
+                          N_("stash message")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_store_usage,
+                            PARSE_OPT_KEEP_UNKNOWN);
+
+       if (argc != 1) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("\"git stash store\" requires one "
+                                            "<commit> argument"));
+               return -1;
+       }
+
+       if (get_oid_with_context(the_repository,
+                                argv[0], quiet ? GET_OID_QUIETLY : 0, &obj,
+                                &dummy)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot update %s with %s"),
+                                            ref_stash, argv[0]);
+               return -1;
+       }
+
+       return do_store_stash(&obj, stash_msg, quiet);
+}
+
+static void add_pathspecs(struct argv_array *args,
+                         const struct pathspec *ps) {
+       int i;
+
+       for (i = 0; i < ps->nr; i++)
+               argv_array_push(args, ps->items[i].original);
+}
+
+/*
+ * `untracked_files` will be filled with the names of untracked files.
+ * The return value is:
+ *
+ * = 0 if there are not any untracked files
+ * > 0 if there are untracked files
+ */
+static int get_untracked_files(const struct pathspec *ps, int include_untracked,
+                              struct strbuf *untracked_files)
+{
+       int i;
+       int max_len;
+       int found = 0;
+       char *seen;
+       struct dir_struct dir;
+
+       memset(&dir, 0, sizeof(dir));
+       if (include_untracked != INCLUDE_ALL_FILES)
+               setup_standard_excludes(&dir);
+
+       seen = xcalloc(ps->nr, 1);
+
+       max_len = fill_directory(&dir, the_repository->index, ps);
+       for (i = 0; i < dir.nr; i++) {
+               struct dir_entry *ent = dir.entries[i];
+               if (dir_path_match(&the_index, ent, ps, max_len, seen)) {
+                       found++;
+                       strbuf_addstr(untracked_files, ent->name);
+                       /* NUL-terminate: will be fed to update-index -z */
+                       strbuf_addch(untracked_files, '\0');
+               }
+               free(ent);
+       }
+
+       free(seen);
+       free(dir.entries);
+       free(dir.ignored);
+       clear_directory(&dir);
+       return found;
+}
+
+/*
+ * The return value of `check_changes_tracked_files()` can be:
+ *
+ * < 0 if there was an error
+ * = 0 if there are no changes.
+ * > 0 if there are changes.
+ */
+static int check_changes_tracked_files(const struct pathspec *ps)
+{
+       int result;
+       struct rev_info rev;
+       struct object_id dummy;
+       int ret = 0;
+
+       /* No initial commit. */
+       if (get_oid("HEAD", &dummy))
+               return -1;
+
+       if (read_cache() < 0)
+               return -1;
+
+       init_revisions(&rev, NULL);
+       copy_pathspec(&rev.prune_data, ps);
+
+       rev.diffopt.flags.quick = 1;
+       rev.diffopt.flags.ignore_submodules = 1;
+       rev.abbrev = 0;
+
+       add_head_to_pending(&rev);
+       diff_setup_done(&rev.diffopt);
+
+       result = run_diff_index(&rev, 1);
+       if (diff_result_code(&rev.diffopt, result)) {
+               ret = 1;
+               goto done;
+       }
+
+       object_array_clear(&rev.pending);
+       result = run_diff_files(&rev, 0);
+       if (diff_result_code(&rev.diffopt, result)) {
+               ret = 1;
+               goto done;
+       }
+
+done:
+       clear_pathspec(&rev.prune_data);
+       return ret;
+}
+
+/*
+ * The function will fill `untracked_files` with the names of untracked files
+ * It will return 1 if there were any changes and 0 if there were not.
+ */
+static int check_changes(const struct pathspec *ps, int include_untracked,
+                        struct strbuf *untracked_files)
+{
+       int ret = 0;
+       if (check_changes_tracked_files(ps))
+               ret = 1;
+
+       if (include_untracked && get_untracked_files(ps, include_untracked,
+                                                    untracked_files))
+               ret = 1;
+
+       return ret;
+}
+
+static int save_untracked_files(struct stash_info *info, struct strbuf *msg,
+                               struct strbuf files)
+{
+       int ret = 0;
+       struct strbuf untracked_msg = STRBUF_INIT;
+       struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+       struct index_state istate = { NULL };
+
+       cp_upd_index.git_cmd = 1;
+       argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+                        "--remove", "--stdin", NULL);
+       argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+
+       strbuf_addf(&untracked_msg, "untracked files on %s\n", msg->buf);
+       if (pipe_command(&cp_upd_index, files.buf, files.len, NULL, 0,
+                        NULL, 0)) {
+               ret = -1;
+               goto done;
+       }
+
+       if (write_index_as_tree(&info->u_tree, &istate, stash_index_path.buf, 0,
+                               NULL)) {
+               ret = -1;
+               goto done;
+       }
+
+       if (commit_tree(untracked_msg.buf, untracked_msg.len,
+                       &info->u_tree, NULL, &info->u_commit, NULL, NULL)) {
+               ret = -1;
+               goto done;
+       }
+
+done:
+       discard_index(&istate);
+       strbuf_release(&untracked_msg);
+       remove_path(stash_index_path.buf);
+       return ret;
+}
+
+static int stash_patch(struct stash_info *info, const struct pathspec *ps,
+                      struct strbuf *out_patch, int quiet)
+{
+       int ret = 0;
+       struct child_process cp_read_tree = CHILD_PROCESS_INIT;
+       struct child_process cp_add_i = CHILD_PROCESS_INIT;
+       struct child_process cp_diff_tree = CHILD_PROCESS_INIT;
+       struct index_state istate = { NULL };
+
+       remove_path(stash_index_path.buf);
+
+       cp_read_tree.git_cmd = 1;
+       argv_array_pushl(&cp_read_tree.args, "read-tree", "HEAD", NULL);
+       argv_array_pushf(&cp_read_tree.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       if (run_command(&cp_read_tree)) {
+               ret = -1;
+               goto done;
+       }
+
+       /* Find out what the user wants. */
+       cp_add_i.git_cmd = 1;
+       argv_array_pushl(&cp_add_i.args, "add--interactive", "--patch=stash",
+                        "--", NULL);
+       add_pathspecs(&cp_add_i.args, ps);
+       argv_array_pushf(&cp_add_i.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+       if (run_command(&cp_add_i)) {
+               ret = -1;
+               goto done;
+       }
+
+       /* State of the working tree. */
+       if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+                               NULL)) {
+               ret = -1;
+               goto done;
+       }
+
+       cp_diff_tree.git_cmd = 1;
+       argv_array_pushl(&cp_diff_tree.args, "diff-tree", "-p", "HEAD",
+                        oid_to_hex(&info->w_tree), "--", NULL);
+       if (pipe_command(&cp_diff_tree, NULL, 0, out_patch, 0, NULL, 0)) {
+               ret = -1;
+               goto done;
+       }
+
+       if (!out_patch->len) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("No changes selected"));
+               ret = 1;
+       }
+
+done:
+       discard_index(&istate);
+       remove_path(stash_index_path.buf);
+       return ret;
+}
+
+static int stash_working_tree(struct stash_info *info, const struct pathspec *ps)
+{
+       int ret = 0;
+       struct rev_info rev;
+       struct child_process cp_upd_index = CHILD_PROCESS_INIT;
+       struct strbuf diff_output = STRBUF_INIT;
+       struct index_state istate = { NULL };
+
+       init_revisions(&rev, NULL);
+       copy_pathspec(&rev.prune_data, ps);
+
+       set_alternate_index_output(stash_index_path.buf);
+       if (reset_tree(&info->i_tree, 0, 0)) {
+               ret = -1;
+               goto done;
+       }
+       set_alternate_index_output(NULL);
+
+       rev.diffopt.output_format = DIFF_FORMAT_CALLBACK;
+       rev.diffopt.format_callback = add_diff_to_buf;
+       rev.diffopt.format_callback_data = &diff_output;
+
+       if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
+               ret = -1;
+               goto done;
+       }
+
+       add_pending_object(&rev, parse_object(the_repository, &info->b_commit),
+                          "");
+       if (run_diff_index(&rev, 0)) {
+               ret = -1;
+               goto done;
+       }
+
+       cp_upd_index.git_cmd = 1;
+       argv_array_pushl(&cp_upd_index.args, "update-index", "-z", "--add",
+                        "--remove", "--stdin", NULL);
+       argv_array_pushf(&cp_upd_index.env_array, "GIT_INDEX_FILE=%s",
+                        stash_index_path.buf);
+
+       if (pipe_command(&cp_upd_index, diff_output.buf, diff_output.len,
+                        NULL, 0, NULL, 0)) {
+               ret = -1;
+               goto done;
+       }
+
+       if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
+                               NULL)) {
+               ret = -1;
+               goto done;
+       }
+
+done:
+       discard_index(&istate);
+       UNLEAK(rev);
+       object_array_clear(&rev.pending);
+       clear_pathspec(&rev.prune_data);
+       strbuf_release(&diff_output);
+       remove_path(stash_index_path.buf);
+       return ret;
+}
+
+static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_buf,
+                          int include_untracked, int patch_mode,
+                          struct stash_info *info, struct strbuf *patch,
+                          int quiet)
+{
+       int ret = 0;
+       int flags = 0;
+       int untracked_commit_option = 0;
+       const char *head_short_sha1 = NULL;
+       const char *branch_ref = NULL;
+       const char *branch_name = "(no branch)";
+       struct commit *head_commit = NULL;
+       struct commit_list *parents = NULL;
+       struct strbuf msg = STRBUF_INIT;
+       struct strbuf commit_tree_label = STRBUF_INIT;
+       struct strbuf untracked_files = STRBUF_INIT;
+
+       prepare_fallback_ident("git stash", "git@stash");
+
+       read_cache_preload(NULL);
+       refresh_cache(REFRESH_QUIET);
+
+       if (get_oid("HEAD", &info->b_commit)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("You do not have "
+                                            "the initial commit yet"));
+               ret = -1;
+               goto done;
+       } else {
+               head_commit = lookup_commit(the_repository, &info->b_commit);
+       }
+
+       if (!check_changes(ps, include_untracked, &untracked_files)) {
+               ret = 1;
+               goto done;
+       }
+
+       branch_ref = resolve_ref_unsafe("HEAD", 0, NULL, &flags);
+       if (flags & REF_ISSYMREF)
+               branch_name = strrchr(branch_ref, '/') + 1;
+       head_short_sha1 = find_unique_abbrev(&head_commit->object.oid,
+                                            DEFAULT_ABBREV);
+       strbuf_addf(&msg, "%s: %s ", branch_name, head_short_sha1);
+       pp_commit_easy(CMIT_FMT_ONELINE, head_commit, &msg);
+
+       strbuf_addf(&commit_tree_label, "index on %s\n", msg.buf);
+       commit_list_insert(head_commit, &parents);
+       if (write_cache_as_tree(&info->i_tree, 0, NULL) ||
+           commit_tree(commit_tree_label.buf, commit_tree_label.len,
+                       &info->i_tree, parents, &info->i_commit, NULL, NULL)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot save the current "
+                                            "index state"));
+               ret = -1;
+               goto done;
+       }
+
+       if (include_untracked) {
+               if (save_untracked_files(info, &msg, untracked_files)) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot save "
+                                                    "the untracked files"));
+                       ret = -1;
+                       goto done;
+               }
+               untracked_commit_option = 1;
+       }
+       if (patch_mode) {
+               ret = stash_patch(info, ps, patch, quiet);
+               if (ret < 0) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot save the current "
+                                                    "worktree state"));
+                       goto done;
+               } else if (ret > 0) {
+                       goto done;
+               }
+       } else {
+               if (stash_working_tree(info, ps)) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot save the current "
+                                                    "worktree state"));
+                       ret = -1;
+                       goto done;
+               }
+       }
+
+       if (!stash_msg_buf->len)
+               strbuf_addf(stash_msg_buf, "WIP on %s", msg.buf);
+       else
+               strbuf_insertf(stash_msg_buf, 0, "On %s: ", branch_name);
+
+       /*
+        * `parents` will be empty after calling `commit_tree()`, so there is
+        * no need to call `free_commit_list()`
+        */
+       parents = NULL;
+       if (untracked_commit_option)
+               commit_list_insert(lookup_commit(the_repository,
+                                                &info->u_commit),
+                                  &parents);
+       commit_list_insert(lookup_commit(the_repository, &info->i_commit),
+                          &parents);
+       commit_list_insert(head_commit, &parents);
+
+       if (commit_tree(stash_msg_buf->buf, stash_msg_buf->len, &info->w_tree,
+                       parents, &info->w_commit, NULL, NULL)) {
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot record "
+                                            "working tree state"));
+               ret = -1;
+               goto done;
+       }
+
+done:
+       strbuf_release(&commit_tree_label);
+       strbuf_release(&msg);
+       strbuf_release(&untracked_files);
+       return ret;
+}
+
+static int create_stash(int argc, const char **argv, const char *prefix)
+{
+       int ret = 0;
+       struct strbuf stash_msg_buf = STRBUF_INIT;
+       struct stash_info info;
+       struct pathspec ps;
+
+       /* Starting with argv[1], since argv[0] is "create" */
+       strbuf_join_argv(&stash_msg_buf, argc - 1, ++argv, ' ');
+
+       memset(&ps, 0, sizeof(ps));
+       if (!check_changes_tracked_files(&ps))
+               return 0;
+
+       ret = do_create_stash(&ps, &stash_msg_buf, 0, 0, &info,
+                             NULL, 0);
+       if (!ret)
+               printf_ln("%s", oid_to_hex(&info.w_commit));
+
+       strbuf_release(&stash_msg_buf);
+       return ret;
+}
+
+static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int quiet,
+                        int keep_index, int patch_mode, int include_untracked)
+{
+       int ret = 0;
+       struct stash_info info;
+       struct strbuf patch = STRBUF_INIT;
+       struct strbuf stash_msg_buf = STRBUF_INIT;
+       struct strbuf untracked_files = STRBUF_INIT;
+
+       if (patch_mode && keep_index == -1)
+               keep_index = 1;
+
+       if (patch_mode && include_untracked) {
+               fprintf_ln(stderr, _("Can't use --patch and --include-untracked"
+                                    " or --all at the same time"));
+               ret = -1;
+               goto done;
+       }
+
+       read_cache_preload(NULL);
+       if (!include_untracked && ps->nr) {
+               int i;
+               char *ps_matched = xcalloc(ps->nr, 1);
+
+               for (i = 0; i < active_nr; i++)
+                       ce_path_match(&the_index, active_cache[i], ps,
+                                     ps_matched);
+
+               if (report_path_error(ps_matched, ps)) {
+                       fprintf_ln(stderr, _("Did you forget to 'git add'?"));
+                       ret = -1;
+                       free(ps_matched);
+                       goto done;
+               }
+               free(ps_matched);
+       }
+
+       if (refresh_cache(REFRESH_QUIET)) {
+               ret = -1;
+               goto done;
+       }
+
+       if (!check_changes(ps, include_untracked, &untracked_files)) {
+               if (!quiet)
+                       printf_ln(_("No local changes to save"));
+               goto done;
+       }
+
+       if (!reflog_exists(ref_stash) && do_clear_stash()) {
+               ret = -1;
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot initialize stash"));
+               goto done;
+       }
+
+       if (stash_msg)
+               strbuf_addstr(&stash_msg_buf, stash_msg);
+       if (do_create_stash(ps, &stash_msg_buf, include_untracked, patch_mode,
+                           &info, &patch, quiet)) {
+               ret = -1;
+               goto done;
+       }
+
+       if (do_store_stash(&info.w_commit, stash_msg_buf.buf, 1)) {
+               ret = -1;
+               if (!quiet)
+                       fprintf_ln(stderr, _("Cannot save the current status"));
+               goto done;
+       }
+
+       if (!quiet)
+               printf_ln(_("Saved working directory and index state %s"),
+                         stash_msg_buf.buf);
+
+       if (!patch_mode) {
+               if (include_untracked && !ps->nr) {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+
+                       cp.git_cmd = 1;
+                       argv_array_pushl(&cp.args, "clean", "--force",
+                                        "--quiet", "-d", NULL);
+                       if (include_untracked == INCLUDE_ALL_FILES)
+                               argv_array_push(&cp.args, "-x");
+                       if (run_command(&cp)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+               discard_cache();
+               if (ps->nr) {
+                       struct child_process cp_add = CHILD_PROCESS_INIT;
+                       struct child_process cp_diff = CHILD_PROCESS_INIT;
+                       struct child_process cp_apply = CHILD_PROCESS_INIT;
+                       struct strbuf out = STRBUF_INIT;
+
+                       cp_add.git_cmd = 1;
+                       argv_array_push(&cp_add.args, "add");
+                       if (!include_untracked)
+                               argv_array_push(&cp_add.args, "-u");
+                       if (include_untracked == INCLUDE_ALL_FILES)
+                               argv_array_push(&cp_add.args, "--force");
+                       argv_array_push(&cp_add.args, "--");
+                       add_pathspecs(&cp_add.args, ps);
+                       if (run_command(&cp_add)) {
+                               ret = -1;
+                               goto done;
+                       }
+
+                       cp_diff.git_cmd = 1;
+                       argv_array_pushl(&cp_diff.args, "diff-index", "-p",
+                                        "--cached", "--binary", "HEAD", "--",
+                                        NULL);
+                       add_pathspecs(&cp_diff.args, ps);
+                       if (pipe_command(&cp_diff, NULL, 0, &out, 0, NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+
+                       cp_apply.git_cmd = 1;
+                       argv_array_pushl(&cp_apply.args, "apply", "--index",
+                                        "-R", NULL);
+                       if (pipe_command(&cp_apply, out.buf, out.len, NULL, 0,
+                                        NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+               } else {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+                       cp.git_cmd = 1;
+                       argv_array_pushl(&cp.args, "reset", "--hard", "-q",
+                                        NULL);
+                       if (run_command(&cp)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+
+               if (keep_index == 1 && !is_null_oid(&info.i_tree)) {
+                       struct child_process cp_ls = CHILD_PROCESS_INIT;
+                       struct child_process cp_checkout = CHILD_PROCESS_INIT;
+                       struct strbuf out = STRBUF_INIT;
+
+                       if (reset_tree(&info.i_tree, 0, 1)) {
+                               ret = -1;
+                               goto done;
+                       }
+
+                       cp_ls.git_cmd = 1;
+                       argv_array_pushl(&cp_ls.args, "ls-files", "-z",
+                                        "--modified", "--", NULL);
+
+                       add_pathspecs(&cp_ls.args, ps);
+                       if (pipe_command(&cp_ls, NULL, 0, &out, 0, NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+
+                       cp_checkout.git_cmd = 1;
+                       argv_array_pushl(&cp_checkout.args, "checkout-index",
+                                        "-z", "--force", "--stdin", NULL);
+                       if (pipe_command(&cp_checkout, out.buf, out.len, NULL,
+                                        0, NULL, 0)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+               goto done;
+       } else {
+               struct child_process cp = CHILD_PROCESS_INIT;
+
+               cp.git_cmd = 1;
+               argv_array_pushl(&cp.args, "apply", "-R", NULL);
+
+               if (pipe_command(&cp, patch.buf, patch.len, NULL, 0, NULL, 0)) {
+                       if (!quiet)
+                               fprintf_ln(stderr, _("Cannot remove "
+                                                    "worktree changes"));
+                       ret = -1;
+                       goto done;
+               }
+
+               if (keep_index < 1) {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+
+                       cp.git_cmd = 1;
+                       argv_array_pushl(&cp.args, "reset", "-q", "--", NULL);
+                       add_pathspecs(&cp.args, ps);
+                       if (run_command(&cp)) {
+                               ret = -1;
+                               goto done;
+                       }
+               }
+               goto done;
+       }
+
+done:
+       strbuf_release(&stash_msg_buf);
+       return ret;
+}
+
+static int push_stash(int argc, const char **argv, const char *prefix)
+{
+       int keep_index = -1;
+       int patch_mode = 0;
+       int include_untracked = 0;
+       int quiet = 0;
+       const char *stash_msg = NULL;
+       struct pathspec ps;
+       struct option options[] = {
+               OPT_BOOL('k', "keep-index", &keep_index,
+                        N_("keep index")),
+               OPT_BOOL('p', "patch", &patch_mode,
+                        N_("stash in patch mode")),
+               OPT__QUIET(&quiet, N_("quiet mode")),
+               OPT_BOOL('u', "include-untracked", &include_untracked,
+                        N_("include untracked files in stash")),
+               OPT_SET_INT('a', "all", &include_untracked,
+                           N_("include ignore files"), 2),
+               OPT_STRING('m', "message", &stash_msg, N_("message"),
+                          N_("stash message")),
+               OPT_END()
+       };
+
+       if (argc)
+               argc = parse_options(argc, argv, prefix, options,
+                                    git_stash_push_usage,
+                                    0);
+
+       parse_pathspec(&ps, 0, PATHSPEC_PREFER_FULL | PATHSPEC_PREFIX_ORIGIN,
+                      prefix, argv);
+       return do_push_stash(&ps, stash_msg, quiet, keep_index, patch_mode,
+                            include_untracked);
+}
+
+static int save_stash(int argc, const char **argv, const char *prefix)
+{
+       int keep_index = -1;
+       int patch_mode = 0;
+       int include_untracked = 0;
+       int quiet = 0;
+       int ret = 0;
+       const char *stash_msg = NULL;
+       struct pathspec ps;
+       struct strbuf stash_msg_buf = STRBUF_INIT;
+       struct option options[] = {
+               OPT_BOOL('k', "keep-index", &keep_index,
+                        N_("keep index")),
+               OPT_BOOL('p', "patch", &patch_mode,
+                        N_("stash in patch mode")),
+               OPT__QUIET(&quiet, N_("quiet mode")),
+               OPT_BOOL('u', "include-untracked", &include_untracked,
+                        N_("include untracked files in stash")),
+               OPT_SET_INT('a', "all", &include_untracked,
+                           N_("include ignore files"), 2),
+               OPT_STRING('m', "message", &stash_msg, "message",
+                          N_("stash message")),
+               OPT_END()
+       };
+
+       argc = parse_options(argc, argv, prefix, options,
+                            git_stash_save_usage,
+                            PARSE_OPT_KEEP_DASHDASH);
+
+       if (argc)
+               stash_msg = strbuf_join_argv(&stash_msg_buf, argc, argv, ' ');
+
+       memset(&ps, 0, sizeof(ps));
+       ret = do_push_stash(&ps, stash_msg, quiet, keep_index,
+                           patch_mode, include_untracked);
+
+       strbuf_release(&stash_msg_buf);
+       return ret;
+}
+
+static int use_builtin_stash(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf out = STRBUF_INIT;
+       int ret, env = git_env_bool("GIT_TEST_STASH_USE_BUILTIN", -1);
+
+       if (env != -1)
+               return env;
+
+       argv_array_pushl(&cp.args,
+                        "config", "--bool", "stash.usebuiltin", NULL);
+       cp.git_cmd = 1;
+       if (capture_command(&cp, &out, 6)) {
+               strbuf_release(&out);
+               return 1;
+       }
+
+       strbuf_trim(&out);
+       ret = !strcmp("true", out.buf);
+       strbuf_release(&out);
+       return ret;
+}
+
+int cmd_stash(int argc, const char **argv, const char *prefix)
+{
+       int i = -1;
+       pid_t pid = getpid();
+       const char *index_file;
+       struct argv_array args = ARGV_ARRAY_INIT;
+
+       struct option options[] = {
+               OPT_END()
+       };
+
+       if (!use_builtin_stash()) {
+               const char *path = mkpath("%s/git-legacy-stash",
+                                         git_exec_path());
+
+               if (sane_execvp(path, (char **)argv) < 0)
+                       die_errno(_("could not exec %s"), path);
+               else
+                       BUG("sane_execvp() returned???");
+       }
+
+       prefix = setup_git_directory();
+       trace_repo_setup(prefix);
+       setup_work_tree();
+
+       git_config(git_diff_basic_config, NULL);
+
+       argc = parse_options(argc, argv, prefix, options, git_stash_usage,
+                            PARSE_OPT_KEEP_UNKNOWN | PARSE_OPT_KEEP_DASHDASH);
+
+       index_file = get_index_file();
+       strbuf_addf(&stash_index_path, "%s.stash.%" PRIuMAX, index_file,
+                   (uintmax_t)pid);
+
+       if (!argc)
+               return !!push_stash(0, NULL, prefix);
+       else if (!strcmp(argv[0], "apply"))
+               return !!apply_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "clear"))
+               return !!clear_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "drop"))
+               return !!drop_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "pop"))
+               return !!pop_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "branch"))
+               return !!branch_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "list"))
+               return !!list_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "show"))
+               return !!show_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "store"))
+               return !!store_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "create"))
+               return !!create_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "push"))
+               return !!push_stash(argc, argv, prefix);
+       else if (!strcmp(argv[0], "save"))
+               return !!save_stash(argc, argv, prefix);
+       else if (*argv[0] != '-')
+               usage_msg_opt(xstrfmt(_("unknown subcommand: %s"), argv[0]),
+                             git_stash_usage, options);
+
+       if (strcmp(argv[0], "-p")) {
+               while (++i < argc && strcmp(argv[i], "--")) {
+                       /*
+                        * `akpqu` is a string which contains all short options,
+                        * except `-m` which is verified separately.
+                        */
+                       if ((strlen(argv[i]) == 2) && *argv[i] == '-' &&
+                           strchr("akpqu", argv[i][1]))
+                               continue;
+
+                       if (!strcmp(argv[i], "--all") ||
+                           !strcmp(argv[i], "--keep-index") ||
+                           !strcmp(argv[i], "--no-keep-index") ||
+                           !strcmp(argv[i], "--patch") ||
+                           !strcmp(argv[i], "--quiet") ||
+                           !strcmp(argv[i], "--include-untracked"))
+                               continue;
+
+                       /*
+                        * `-m` and `--message=` are verified separately because
+                        * they need to be immediately followed by a string
+                        * (i.e.`-m"foobar"` or `--message="foobar"`).
+                        */
+                       if (starts_with(argv[i], "-m") ||
+                           starts_with(argv[i], "--message="))
+                               continue;
+
+                       usage_with_options(git_stash_usage, options);
+               }
+       }
+
+       argv_array_push(&args, "push");
+       argv_array_pushv(&args, argv);
+       return !!push_stash(args.argc, args.argv, prefix);
+}
index ff5cfd11948593984c791a0f922351343019f4cf..890c251d665a1b6800b87cc6ee0fcb04a3f9f538 100644 (file)
@@ -1081,6 +1081,8 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        if (entries < 0)
                die("cache corrupted");
 
+       the_index.updated_skipworktree = 1;
+
        /*
         * Custom copy of parse_options() because we want to handle
         * filename arguments as they come.
index 6cc094a453806308442d054a7d1c073ecf18b87b..d2a7e2f3f18ba411d065a52ac868db612de5beaa 100644 (file)
@@ -268,10 +268,10 @@ static int add_worktree(const char *path, const char *refname,
        struct strbuf sb_git = STRBUF_INIT, sb_repo = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
        const char *name;
-       struct stat st;
        struct child_process cp = CHILD_PROCESS_INIT;
        struct argv_array child_env = ARGV_ARRAY_INIT;
-       int counter = 0, len, ret;
+       unsigned int counter = 0;
+       int len, ret;
        struct strbuf symref = STRBUF_INIT;
        struct commit *commit = NULL;
        int is_branch = 0;
@@ -295,8 +295,12 @@ static int add_worktree(const char *path, const char *refname,
        if (safe_create_leading_directories_const(sb_repo.buf))
                die_errno(_("could not create leading directories of '%s'"),
                          sb_repo.buf);
-       while (!stat(sb_repo.buf, &st)) {
+
+       while (mkdir(sb_repo.buf, 0777)) {
                counter++;
+               if ((errno != EEXIST) || !counter /* overflow */)
+                       die_errno(_("could not create directory of '%s'"),
+                                 sb_repo.buf);
                strbuf_setlen(&sb_repo, len);
                strbuf_addf(&sb_repo, "%d", counter);
        }
@@ -306,8 +310,6 @@ static int add_worktree(const char *path, const char *refname,
        atexit(remove_junk);
        sigchain_push_common(remove_junk_on_signal);
 
-       if (mkdir(sb_repo.buf, 0777))
-               die_errno(_("could not create directory of '%s'"), sb_repo.buf);
        junk_git_dir = xstrdup(sb_repo.buf);
        is_junk = 1;
 
diff --git a/cache.h b/cache.h
index ac92421f3a8bc814da3f01731f2d8d8a52dc8364..e928fe9d3bd90b3ac2ca69bd31d281784287d536 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -339,7 +339,9 @@ struct index_state {
        struct cache_time timestamp;
        unsigned name_hash_initialized : 1,
                 initialized : 1,
-                drop_cache_tree : 1;
+                drop_cache_tree : 1,
+                updated_workdir : 1,
+                updated_skipworktree : 1;
        struct hashmap name_hash;
        struct hashmap dir_hash;
        struct object_id oid;
@@ -1379,6 +1381,7 @@ enum get_oid_result {
 };
 
 extern int get_oid(const char *str, struct object_id *oid);
+extern int get_oidf(struct object_id *oid, const char *fmt, ...);
 extern int get_oid_commit(const char *str, struct object_id *oid);
 extern int get_oid_committish(const char *str, struct object_id *oid);
 extern int get_oid_tree(const char *str, struct object_id *oid);
@@ -1552,6 +1555,10 @@ extern const char *git_sequence_editor(void);
 extern const char *git_pager(int stdout_is_tty);
 extern int is_terminal_dumb(void);
 extern int git_ident_config(const char *, const char *, void *);
+/*
+ * Prepare an ident to fall back on if the user didn't configure it.
+ */
+void prepare_fallback_ident(const char *name, const char *email);
 extern void reset_ident_date(void);
 
 struct ident_split {
index d64667fcbfef80e4603d97f7825399dea5be0b6b..52a44c690a7a38850e77eb1525e6f8c56e782369 100755 (executable)
@@ -54,6 +54,9 @@ StaticAnalysis)
 Documentation)
        sudo apt-get -q update
        sudo apt-get -q -y install asciidoc xmlto
+
+       test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
+       gem install --version 1.5.8 asciidoctor
        ;;
 esac
 
index be3b7d376ac47391be71a3822a320e327f7a3a2d..d49089832de08f2fcefa1a8b2c45bb668964356a 100755 (executable)
@@ -5,32 +5,38 @@
 
 . ${0%/*}/lib.sh
 
-test -n "$ALREADY_HAVE_ASCIIDOCTOR" ||
-gem install asciidoctor
+filter_log () {
+       sed -e '/^GIT_VERSION = /d' \
+           -e '/^    \* new asciidoc flags$/d' \
+           "$1"
+}
 
 make check-builtins
 make check-docs
 
 # Build docs with AsciiDoc
-make doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
-! test -s stderr.log
+make doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
+cat stderr.raw
+filter_log stderr.raw >stderr.log
+test ! -s stderr.log
 test -s Documentation/git.html
 test -s Documentation/git.xml
 test -s Documentation/git.1
 grep '<meta name="generator" content="AsciiDoc ' Documentation/git.html
 
-rm -f stdout.log stderr.log
+rm -f stdout.log stderr.log stderr.raw
 check_unignored_build_artifacts
 
 # Build docs with AsciiDoctor
 make clean
-make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
-sed '/^GIT_VERSION = / d' stderr.log
-! test -s stderr.log
+make USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.raw >&2)
+cat stderr.raw
+filter_log stderr.raw >stderr.log
+test ! -s stderr.log
 test -s Documentation/git.html
 grep '<meta name="generator" content="Asciidoctor ' Documentation/git.html
 
-rm -f stdout.log stderr.log
+rm -f stdout.log stderr.log stderr.raw
 check_unignored_build_artifacts
 
 save_good_tree
index 0f0cdd8c0fc9ee40c1f56d1935c1816eb03601ad..c2846df3f1d3be68b376ea154e2607726f32d01c 100644 (file)
--- a/config.c
+++ b/config.c
@@ -242,7 +242,7 @@ static int include_by_gitdir(const struct config_options *opts,
        }
 
        ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
-                        icase ? WM_CASEFOLD : 0);
+                        WM_PATHNAME | (icase ? WM_CASEFOLD : 0));
 
        if (!ret && !already_tried_absolute) {
                /*
diff --git a/contrib/coccinelle/flex_alloc.cocci b/contrib/coccinelle/flex_alloc.cocci
new file mode 100644 (file)
index 0000000..e9f7f6d
--- /dev/null
@@ -0,0 +1,13 @@
+@@
+expression str;
+identifier x, flexname;
+@@
+- FLEX_ALLOC_MEM(x, flexname, str, strlen(str));
++ FLEX_ALLOC_STR(x, flexname, str);
+
+@@
+expression str;
+identifier x, ptrname;
+@@
+- FLEXPTR_ALLOC_MEM(x, ptrname, str, strlen(str));
++ FLEXPTR_ALLOC_STR(x, ptrname, str);
index 976e4a6548fdedf630b2f5e4539df1f43d83809f..42a3c8e52417d42a255c68a4f6f24a1518bb3404 100644 (file)
@@ -1024,7 +1024,7 @@ __git_all_commands=
 __git_compute_all_commands ()
 {
        test -n "$__git_all_commands" ||
-       __git_all_commands=$(git --list-cmds=main,others,alias,nohelpers)
+       __git_all_commands=$(__git --list-cmds=main,others,alias,nohelpers)
 }
 
 # Lists all set config variables starting with the given section prefix,
@@ -1501,7 +1501,8 @@ _git_diff ()
 }
 
 __git_mergetools_common="diffuse diffmerge ecmerge emerge kdiff3 meld opendiff
-                       tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc codecompare
+                       tkdiff vimdiff gvimdiff xxdiff araxis p4merge bc
+                       codecompare smerge
 "
 
 _git_difftool ()
@@ -1652,9 +1653,9 @@ _git_help ()
        esac
        if test -n "$GIT_TESTING_ALL_COMMAND_LIST"
        then
-               __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(git --list-cmds=alias,list-guide) gitk"
+               __gitcomp "$GIT_TESTING_ALL_COMMAND_LIST $(__git --list-cmds=alias,list-guide) gitk"
        else
-               __gitcomp "$(git --list-cmds=main,nohelpers,alias,list-guide) gitk"
+               __gitcomp "$(__git --list-cmds=main,nohelpers,alias,list-guide) gitk"
        fi
 }
 
@@ -2925,7 +2926,7 @@ __git_main ()
                        then
                                __gitcomp "$GIT_TESTING_PORCELAIN_COMMAND_LIST"
                        else
-                               __gitcomp "$(git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
+                               __gitcomp "$(__git --list-cmds=list-mainporcelain,others,nohelpers,alias,list-complete,config)"
                        fi
                        ;;
                esac
index 147201dc6c5786c9cfedbbbff0df0b1dced171db..868e18b9a1ab85ae093da936558d394b37059215 100755 (executable)
@@ -14,7 +14,7 @@ git subtree add   --prefix=<prefix> <repository> <ref>
 git subtree merge --prefix=<prefix> <commit>
 git subtree pull  --prefix=<prefix> <repository> <ref>
 git subtree push  --prefix=<prefix> <repository> <ref>
-git subtree split --prefix=<prefix> <commit...>
+git subtree split --prefix=<prefix> <commit>
 --
 h,help        show the help
 q             quiet
@@ -77,6 +77,12 @@ assert () {
        fi
 }
 
+ensure_single_rev () {
+       if test $# -ne 1
+       then
+               die "You must provide exactly one revision.  Got: '$@'"
+       fi
+}
 
 while test $# -gt 0
 do
@@ -185,6 +191,7 @@ if test "$command" != "pull" &&
 then
        revs=$(git rev-parse $default --revs-only "$@") || exit $?
        dirs=$(git rev-parse --no-revs --no-flags "$@") || exit $?
+       ensure_single_rev $revs
        if test -n "$dirs"
        then
                die "Error: Use --prefix instead of bare filenames."
@@ -716,9 +723,8 @@ cmd_add_repository () {
 }
 
 cmd_add_commit () {
-       revs=$(git rev-parse $default --revs-only "$@") || exit $?
-       set -- $revs
-       rev="$1"
+       rev=$(git rev-parse $default --revs-only "$@") || exit $?
+       ensure_single_rev $rev
 
        debug "Adding $dir as '$rev'..."
        git read-tree --prefix="$dir" $rev || exit $?
@@ -817,16 +823,10 @@ cmd_split () {
 }
 
 cmd_merge () {
-       revs=$(git rev-parse $default --revs-only "$@") || exit $?
+       rev=$(git rev-parse $default --revs-only "$@") || exit $?
+       ensure_single_rev $rev
        ensure_clean
 
-       set -- $revs
-       if test $# -ne 1
-       then
-               die "You must provide exactly one revision.  Got: '$revs'"
-       fi
-       rev="$1"
-
        if test -n "$squash"
        then
                first_split="$(find_latest_squash "$dir")"
index 5d0307fc1004f215c48a6f653e21da41bee71baa..94ff8376492257782a1af5b3d2851ee8724c2edd 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -731,7 +731,7 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le
        if (start_async(&async))
                return 0;       /* error was already reported */
 
-       if (strbuf_read(&nbuf, async.out, len) < 0) {
+       if (strbuf_read(&nbuf, async.out, 0) < 0) {
                err = error(_("read from external filter '%s' failed"), cmd);
        }
        if (close(async.out)) {
diff --git a/diff.c b/diff.c
index ec5c095199b3953cc74dabdfccf4536d4c6df831..6dfad79f1d67e9659eaaa207bf7a3e7c6a596a6b 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -4788,14 +4788,6 @@ static int parse_dirstat_opt(struct diff_options *options, const char *params)
        return 1;
 }
 
-static int parse_submodule_opt(struct diff_options *options, const char *value)
-{
-       if (parse_submodule_params(options, value))
-               die(_("Failed to parse --submodule option parameter: '%s'"),
-                       value);
-       return 1;
-}
-
 static const char diff_status_letters[] = {
        DIFF_STATUS_ADDED,
        DIFF_STATUS_COPIED,
@@ -4906,6 +4898,31 @@ static int parse_objfind_opt(struct diff_options *opt, const char *arg)
        return 1;
 }
 
+static int diff_opt_anchored(const struct option *opt,
+                            const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
+       ALLOC_GROW(options->anchors, options->anchors_nr + 1,
+                  options->anchors_alloc);
+       options->anchors[options->anchors_nr++] = xstrdup(arg);
+       return 0;
+}
+
+static int diff_opt_binary(const struct option *opt,
+                          const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
+       enable_patch_output(&options->output_format);
+       options->flags.binary = 1;
+       return 0;
+}
+
 static int diff_opt_break_rewrites(const struct option *opt,
                                   const char *arg, int unset)
 {
@@ -4943,6 +4960,18 @@ static int diff_opt_char(const struct option *opt,
        return 0;
 }
 
+static int diff_opt_color_words(const struct option *opt,
+                               const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       options->use_color = 1;
+       options->word_diff = DIFF_WORDS_COLOR;
+       options->word_regex = arg;
+       return 0;
+}
+
 static int diff_opt_compact_summary(const struct option *opt,
                                    const char *arg, int unset)
 {
@@ -4958,6 +4987,24 @@ static int diff_opt_compact_summary(const struct option *opt,
        return 0;
 }
 
+static int diff_opt_diff_algorithm(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+       long value = parse_algorithm_value(arg);
+
+       BUG_ON_OPT_NEG(unset);
+       if (value < 0)
+               return error(_("option diff-algorithm accepts \"myers\", "
+                              "\"minimal\", \"patience\" and \"histogram\""));
+
+       /* clear out previous settings */
+       DIFF_XDL_CLR(options, NEED_MINIMAL);
+       options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
+       options->xdl_opts |= value;
+       return 0;
+}
+
 static int diff_opt_dirstat(const struct option *opt,
                            const char *arg, int unset)
 {
@@ -5010,6 +5057,34 @@ static int diff_opt_find_renames(const struct option *opt,
        return 0;
 }
 
+static int diff_opt_follow(const struct option *opt,
+                          const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_ARG(arg);
+       if (unset) {
+               options->flags.follow_renames = 0;
+               options->flags.default_follow_renames = 0;
+       } else {
+               options->flags.follow_renames = 1;
+       }
+       return 0;
+}
+
+static int diff_opt_ignore_submodules(const struct option *opt,
+                                     const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       if (!arg)
+               arg = "all";
+       options->flags.override_submodule_config = 1;
+       handle_ignore_submodules_arg(options, arg);
+       return 0;
+}
+
 static enum parse_opt_result diff_opt_output(struct parse_opt_ctx_t *ctx,
                                             const struct option *opt,
                                             const char *arg, int unset)
@@ -5027,6 +5102,26 @@ static enum parse_opt_result diff_opt_output(struct parse_opt_ctx_t *ctx,
        return 0;
 }
 
+static int diff_opt_patience(const struct option *opt,
+                            const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+       int i;
+
+       BUG_ON_OPT_NEG(unset);
+       BUG_ON_OPT_ARG(arg);
+       options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
+       /*
+        * Both --patience and --anchored use PATIENCE_DIFF
+        * internally, so remove any anchors previously
+        * specified.
+        */
+       for (i = 0; i < options->anchors_nr; i++)
+               free(options->anchors[i]);
+       options->anchors_nr = 0;
+       return 0;
+}
+
 static int diff_opt_relative(const struct option *opt,
                             const char *arg, int unset)
 {
@@ -5039,6 +5134,35 @@ static int diff_opt_relative(const struct option *opt,
        return 0;
 }
 
+static int diff_opt_submodule(const struct option *opt,
+                             const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       if (!arg)
+               arg = "log";
+       if (parse_submodule_params(options, arg))
+               return error(_("failed to parse --submodule option parameter: '%s'"),
+                            arg);
+       return 0;
+}
+
+static int diff_opt_textconv(const struct option *opt,
+                            const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_ARG(arg);
+       if (unset) {
+               options->flags.allow_textconv = 0;
+       } else {
+               options->flags.allow_textconv = 1;
+               options->flags.textconv_set_via_cmdline = 1;
+       }
+       return 0;
+}
+
 static int diff_opt_unified(const struct option *opt,
                            const char *arg, int unset)
 {
@@ -5055,6 +5179,44 @@ static int diff_opt_unified(const struct option *opt,
        return 0;
 }
 
+static int diff_opt_word_diff(const struct option *opt,
+                             const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       if (arg) {
+               if (!strcmp(arg, "plain"))
+                       options->word_diff = DIFF_WORDS_PLAIN;
+               else if (!strcmp(arg, "color")) {
+                       options->use_color = 1;
+                       options->word_diff = DIFF_WORDS_COLOR;
+               }
+               else if (!strcmp(arg, "porcelain"))
+                       options->word_diff = DIFF_WORDS_PORCELAIN;
+               else if (!strcmp(arg, "none"))
+                       options->word_diff = DIFF_WORDS_NONE;
+               else
+                       return error(_("bad --word-diff argument: %s"), arg);
+       } else {
+               if (options->word_diff == DIFF_WORDS_NONE)
+                       options->word_diff = DIFF_WORDS_PLAIN;
+       }
+       return 0;
+}
+
+static int diff_opt_word_diff_regex(const struct option *opt,
+                                   const char *arg, int unset)
+{
+       struct diff_options *options = opt->value;
+
+       BUG_ON_OPT_NEG(unset);
+       if (options->word_diff == DIFF_WORDS_NONE)
+               options->word_diff = DIFF_WORDS_PLAIN;
+       options->word_regex = arg;
+       return 0;
+}
+
 static void prep_parse_options(struct diff_options *options)
 {
        struct option parseopts[] = {
@@ -5132,6 +5294,13 @@ static void prep_parse_options(struct diff_options *options)
                OPT_CALLBACK_F(0, "compact-summary", options, NULL,
                               N_("generate compact summary in diffstat"),
                               PARSE_OPT_NOARG, diff_opt_compact_summary),
+               OPT_CALLBACK_F(0, "binary", options, NULL,
+                              N_("output a binary diff that can be applied"),
+                              PARSE_OPT_NONEG | PARSE_OPT_NOARG, diff_opt_binary),
+               OPT_BOOL(0, "full-index", &options->flags.full_index,
+                        N_("show full pre- and post-image object names on the \"index\" lines")),
+               OPT_COLOR_FLAG(0, "color", &options->use_color,
+                              N_("show colored diff")),
                OPT_CALLBACK_F(0, "output-indicator-new",
                               &options->output_indicators[OUTPUT_INDICATOR_NEW],
                               N_("<char>"),
@@ -5171,6 +5340,9 @@ static void prep_parse_options(struct diff_options *options)
                              0, PARSE_OPT_NONEG),
                OPT_BOOL(0, "rename-empty", &options->flags.rename_empty,
                         N_("use empty blobs as rename source")),
+               OPT_CALLBACK_F(0, "follow", options, NULL,
+                              N_("continue listing the history of a file beyond renames"),
+                              PARSE_OPT_NOARG, diff_opt_follow),
 
                OPT_GROUP(N_("Diff algorithm options")),
                OPT_BIT(0, "minimal", &options->xdl_opts,
@@ -5191,12 +5363,58 @@ static void prep_parse_options(struct diff_options *options)
                OPT_BIT_F(0, "ignore-blank-lines", &options->xdl_opts,
                          N_("ignore changes whose lines are all blank"),
                          XDF_IGNORE_BLANK_LINES, PARSE_OPT_NONEG),
+               OPT_BIT(0, "indent-heuristic", &options->xdl_opts,
+                       N_("heuristic to shift diff hunk boundaries for easy reading"),
+                       XDF_INDENT_HEURISTIC),
+               OPT_CALLBACK_F(0, "patience", options, NULL,
+                              N_("generate diff using the \"patience diff\" algorithm"),
+                              PARSE_OPT_NONEG | PARSE_OPT_NOARG,
+                              diff_opt_patience),
+               OPT_BITOP(0, "histogram", &options->xdl_opts,
+                         N_("generate diff using the \"histogram diff\" algorithm"),
+                         XDF_HISTOGRAM_DIFF, XDF_DIFF_ALGORITHM_MASK),
+               OPT_CALLBACK_F(0, "diff-algorithm", options, N_("<algorithm>"),
+                              N_("choose a diff algorithm"),
+                              PARSE_OPT_NONEG, diff_opt_diff_algorithm),
+               OPT_CALLBACK_F(0, "anchored", options, N_("<text>"),
+                              N_("generate diff using the \"anchored diff\" algorithm"),
+                              PARSE_OPT_NONEG, diff_opt_anchored),
+               OPT_CALLBACK_F(0, "word-diff", options, N_("<mode>"),
+                              N_("show word diff, using <mode> to delimit changed words"),
+                              PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_word_diff),
+               OPT_CALLBACK_F(0, "word-diff-regex", options, N_("<regex>"),
+                              N_("use <regex> to decide what a word is"),
+                              PARSE_OPT_NONEG, diff_opt_word_diff_regex),
+               OPT_CALLBACK_F(0, "color-words", options, N_("<regex>"),
+                              N_("equivalent to --word-diff=color --word-diff-regex=<regex>"),
+                              PARSE_OPT_NONEG | PARSE_OPT_OPTARG, diff_opt_color_words),
 
                OPT_GROUP(N_("Diff other options")),
                OPT_CALLBACK_F(0, "relative", options, N_("<prefix>"),
                               N_("when run from subdir, exclude changes outside and show relative paths"),
                               PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
                               diff_opt_relative),
+               OPT_BOOL('a', "text", &options->flags.text,
+                        N_("treat all files as text")),
+               OPT_BOOL('R', NULL, &options->flags.reverse_diff,
+                        N_("swap two inputs, reverse the diff")),
+               OPT_BOOL(0, "exit-code", &options->flags.exit_with_status,
+                        N_("exit with 1 if there were differences, 0 otherwise")),
+               OPT_BOOL(0, "quiet", &options->flags.quick,
+                        N_("disable all output of the program")),
+               OPT_BOOL(0, "ext-diff", &options->flags.allow_external,
+                        N_("allow an external diff helper to be executed")),
+               OPT_CALLBACK_F(0, "textconv", options, NULL,
+                              N_("run external text conversion filters when comparing binary files"),
+                              PARSE_OPT_NOARG, diff_opt_textconv),
+               OPT_CALLBACK_F(0, "ignore-submodules", options, N_("<when>"),
+                              N_("ignore changes to submodules in the diff generation"),
+                              PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
+                              diff_opt_ignore_submodules),
+               OPT_CALLBACK_F(0, "submodule", options, N_("<format>"),
+                              N_("specify how differences in submodules are shown"),
+                              PARSE_OPT_NONEG | PARSE_OPT_OPTARG,
+                              diff_opt_submodule),
                { OPTION_CALLBACK, 0, "output", options, N_("<file>"),
                  N_("Output to a specific file"),
                  PARSE_OPT_NONEG, NULL, 0, diff_opt_output },
@@ -5228,66 +5446,8 @@ int diff_opt_parse(struct diff_options *options,
        if (ac)
                return ac;
 
-       /* xdiff options */
-       if (!strcmp(arg, "--indent-heuristic"))
-               DIFF_XDL_SET(options, INDENT_HEURISTIC);
-       else if (!strcmp(arg, "--no-indent-heuristic"))
-               DIFF_XDL_CLR(options, INDENT_HEURISTIC);
-       else if (!strcmp(arg, "--patience")) {
-               int i;
-               options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
-               /*
-                * Both --patience and --anchored use PATIENCE_DIFF
-                * internally, so remove any anchors previously
-                * specified.
-                */
-               for (i = 0; i < options->anchors_nr; i++)
-                       free(options->anchors[i]);
-               options->anchors_nr = 0;
-       } else if (!strcmp(arg, "--histogram"))
-               options->xdl_opts = DIFF_WITH_ALG(options, HISTOGRAM_DIFF);
-       else if ((argcount = parse_long_opt("diff-algorithm", av, &optarg))) {
-               long value = parse_algorithm_value(optarg);
-               if (value < 0)
-                       return error("option diff-algorithm accepts \"myers\", "
-                                    "\"minimal\", \"patience\" and \"histogram\"");
-               /* clear out previous settings */
-               DIFF_XDL_CLR(options, NEED_MINIMAL);
-               options->xdl_opts &= ~XDF_DIFF_ALGORITHM_MASK;
-               options->xdl_opts |= value;
-               return argcount;
-       } else if (skip_prefix(arg, "--anchored=", &arg)) {
-               options->xdl_opts = DIFF_WITH_ALG(options, PATIENCE_DIFF);
-               ALLOC_GROW(options->anchors, options->anchors_nr + 1,
-                          options->anchors_alloc);
-               options->anchors[options->anchors_nr++] = xstrdup(arg);
-       }
-
        /* flags options */
-       else if (!strcmp(arg, "--binary")) {
-               enable_patch_output(&options->output_format);
-               options->flags.binary = 1;
-       }
-       else if (!strcmp(arg, "--full-index"))
-               options->flags.full_index = 1;
-       else if (!strcmp(arg, "-a") || !strcmp(arg, "--text"))
-               options->flags.text = 1;
-       else if (!strcmp(arg, "-R"))
-               options->flags.reverse_diff = 1;
-       else if (!strcmp(arg, "--follow"))
-               options->flags.follow_renames = 1;
-       else if (!strcmp(arg, "--no-follow")) {
-               options->flags.follow_renames = 0;
-               options->flags.default_follow_renames = 0;
-       } else if (skip_to_optional_arg_default(arg, "--color", &arg, "always")) {
-               int value = git_config_colorbool(NULL, arg);
-               if (value < 0)
-                       return error("option `color' expects \"always\", \"auto\", or \"never\"");
-               options->use_color = value;
-       }
-       else if (!strcmp(arg, "--no-color"))
-               options->use_color = 0;
-       else if (!strcmp(arg, "--color-moved")) {
+       if (!strcmp(arg, "--color-moved")) {
                if (diff_color_moved_default)
                        options->color_moved = diff_color_moved_default;
                if (options->color_moved == COLOR_MOVED_NO)
@@ -5306,53 +5466,7 @@ int diff_opt_parse(struct diff_options *options,
                if (cm & COLOR_MOVED_WS_ERROR)
                        return -1;
                options->color_moved_ws_handling = cm;
-       } else if (skip_to_optional_arg_default(arg, "--color-words", &options->word_regex, NULL)) {
-               options->use_color = 1;
-               options->word_diff = DIFF_WORDS_COLOR;
-       }
-       else if (!strcmp(arg, "--word-diff")) {
-               if (options->word_diff == DIFF_WORDS_NONE)
-                       options->word_diff = DIFF_WORDS_PLAIN;
-       }
-       else if (skip_prefix(arg, "--word-diff=", &arg)) {
-               if (!strcmp(arg, "plain"))
-                       options->word_diff = DIFF_WORDS_PLAIN;
-               else if (!strcmp(arg, "color")) {
-                       options->use_color = 1;
-                       options->word_diff = DIFF_WORDS_COLOR;
-               }
-               else if (!strcmp(arg, "porcelain"))
-                       options->word_diff = DIFF_WORDS_PORCELAIN;
-               else if (!strcmp(arg, "none"))
-                       options->word_diff = DIFF_WORDS_NONE;
-               else
-                       die("bad --word-diff argument: %s", arg);
-       }
-       else if ((argcount = parse_long_opt("word-diff-regex", av, &optarg))) {
-               if (options->word_diff == DIFF_WORDS_NONE)
-                       options->word_diff = DIFF_WORDS_PLAIN;
-               options->word_regex = optarg;
-               return argcount;
-       }
-       else if (!strcmp(arg, "--exit-code"))
-               options->flags.exit_with_status = 1;
-       else if (!strcmp(arg, "--quiet"))
-               options->flags.quick = 1;
-       else if (!strcmp(arg, "--ext-diff"))
-               options->flags.allow_external = 1;
-       else if (!strcmp(arg, "--no-ext-diff"))
-               options->flags.allow_external = 0;
-       else if (!strcmp(arg, "--textconv")) {
-               options->flags.allow_textconv = 1;
-               options->flags.textconv_set_via_cmdline = 1;
-       } else if (!strcmp(arg, "--no-textconv"))
-               options->flags.allow_textconv = 0;
-       else if (skip_to_optional_arg_default(arg, "--ignore-submodules", &arg, "all")) {
-               options->flags.override_submodule_config = 1;
-               handle_ignore_submodules_arg(options, arg);
-       } else if (skip_to_optional_arg_default(arg, "--submodule", &arg, "log"))
-               return parse_submodule_opt(options, arg);
-       else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
+       } else if (skip_prefix(arg, "--ws-error-highlight=", &arg))
                return parse_ws_error_highlight_opt(options, arg);
        else if (!strcmp(arg, "--ita-invisible-in-index"))
                options->ita_invisible_in_index = 1;
diff --git a/git-legacy-rebase.sh b/git-legacy-rebase.sh
deleted file mode 100755 (executable)
index 5c2c4e5..0000000
+++ /dev/null
@@ -1,770 +0,0 @@
-#!/bin/sh
-#
-# Copyright (c) 2005 Junio C Hamano.
-#
-
-SUBDIRECTORY_OK=Yes
-OPTIONS_KEEPDASHDASH=
-OPTIONS_STUCKLONG=t
-OPTIONS_SPEC="\
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] [<upstream>] [<branch>]
-git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] --root [<branch>]
-git rebase --continue | --abort | --skip | --edit-todo
---
- Available options are
-v,verbose!         display a diffstat of what changed upstream
-q,quiet!           be quiet. implies --no-stat
-autostash          automatically stash/stash pop before and after
-fork-point         use 'merge-base --fork-point' to refine upstream
-onto=!             rebase onto given branch instead of upstream
-r,rebase-merges?   try to rebase merges instead of skipping them
-p,preserve-merges! try to recreate merges instead of ignoring them
-s,strategy=!       use the given merge strategy
-X,strategy-option=! pass the argument through to the merge strategy
-no-ff!             cherry-pick all commits, even if unchanged
-f,force-rebase!    cherry-pick all commits, even if unchanged
-m,merge!           use merging strategies to rebase
-i,interactive!     let the user edit the list of commits to rebase
-x,exec=!           add exec lines after each commit of the editable list
-k,keep-empty      preserve empty commits during rebase
-allow-empty-message allow rebasing commits with empty messages
-stat!              display a diffstat of what changed upstream
-n,no-stat!         do not show diffstat of what changed upstream
-verify             allow pre-rebase hook to run
-rerere-autoupdate  allow rerere to update index with resolved conflicts
-root!              rebase all reachable commits up to the root(s)
-autosquash         move commits that begin with squash!/fixup! under -i
-signoff            add a Signed-off-by: line to each commit
-committer-date-is-author-date! passed to 'git am'
-ignore-date!       passed to 'git am'
-whitespace=!       passed to 'git apply'
-ignore-whitespace! passed to 'git apply'
-C=!                passed to 'git apply'
-S,gpg-sign?        GPG-sign commits
- Actions:
-continue!          continue
-abort!             abort and check out the original branch
-skip!              skip current patch and continue
-edit-todo!         edit the todo list during an interactive rebase
-quit!              abort but keep HEAD where it is
-show-current-patch! show the patch file being applied or merged
-reschedule-failed-exec automatically reschedule failed exec commands
-"
-. git-sh-setup
-set_reflog_action rebase
-require_work_tree_exists
-cd_to_toplevel
-
-LF='
-'
-ok_to_skip_pre_rebase=
-
-squash_onto=
-unset onto
-unset restrict_revision
-cmd=
-strategy=
-strategy_opts=
-do_merge=
-merge_dir="$GIT_DIR"/rebase-merge
-apply_dir="$GIT_DIR"/rebase-apply
-verbose=
-diffstat=
-test "$(git config --bool rebase.stat)" = true && diffstat=t
-autostash="$(git config --bool rebase.autostash || echo false)"
-fork_point=auto
-git_am_opt=
-git_format_patch_opt=
-rebase_root=
-force_rebase=
-allow_rerere_autoupdate=
-# Non-empty if a rebase was in progress when 'git rebase' was invoked
-in_progress=
-# One of {am, merge, interactive}
-type=
-# One of {"$GIT_DIR"/rebase-apply, "$GIT_DIR"/rebase-merge}
-state_dir=
-# One of {'', continue, skip, abort}, as parsed from command line
-action=
-rebase_merges=
-rebase_cousins=
-preserve_merges=
-autosquash=
-keep_empty=
-allow_empty_message=--allow-empty-message
-signoff=
-reschedule_failed_exec=
-test "$(git config --bool rebase.autosquash)" = "true" && autosquash=t
-case "$(git config --bool commit.gpgsign)" in
-true)  gpg_sign_opt=-S ;;
-*)     gpg_sign_opt= ;;
-esac
-test "$(git config --bool rebase.reschedulefailedexec)" = "true" &&
-reschedule_failed_exec=--reschedule-failed-exec
-. git-rebase--common
-
-read_basic_state () {
-       test -f "$state_dir/head-name" &&
-       test -f "$state_dir/onto" &&
-       head_name=$(cat "$state_dir"/head-name) &&
-       onto=$(cat "$state_dir"/onto) &&
-       # We always write to orig-head, but interactive rebase used to write to
-       # head. Fall back to reading from head to cover for the case that the
-       # user upgraded git with an ongoing interactive rebase.
-       if test -f "$state_dir"/orig-head
-       then
-               orig_head=$(cat "$state_dir"/orig-head)
-       else
-               orig_head=$(cat "$state_dir"/head)
-       fi &&
-       test -f "$state_dir"/quiet && GIT_QUIET=t
-       test -f "$state_dir"/verbose && verbose=t
-       test -f "$state_dir"/strategy && strategy="$(cat "$state_dir"/strategy)"
-       test -f "$state_dir"/strategy_opts &&
-               strategy_opts="$(cat "$state_dir"/strategy_opts)"
-       test -f "$state_dir"/allow_rerere_autoupdate &&
-               allow_rerere_autoupdate="$(cat "$state_dir"/allow_rerere_autoupdate)"
-       test -f "$state_dir"/gpg_sign_opt &&
-               gpg_sign_opt="$(cat "$state_dir"/gpg_sign_opt)"
-       test -f "$state_dir"/signoff && {
-               signoff="$(cat "$state_dir"/signoff)"
-               force_rebase=t
-       }
-       test -f "$state_dir"/reschedule-failed-exec &&
-               reschedule_failed_exec=t
-}
-
-finish_rebase () {
-       rm -f "$(git rev-parse --git-path REBASE_HEAD)"
-       apply_autostash &&
-       { git gc --auto || true; } &&
-       rm -rf "$state_dir"
-}
-
-run_interactive () {
-       GIT_CHERRY_PICK_HELP="$resolvemsg"
-       export GIT_CHERRY_PICK_HELP
-
-       test -n "$keep_empty" && keep_empty="--keep-empty"
-       test -n "$rebase_merges" && rebase_merges="--rebase-merges"
-       test -n "$rebase_cousins" && rebase_cousins="--rebase-cousins"
-       test -n "$autosquash" && autosquash="--autosquash"
-       test -n "$verbose" && verbose="--verbose"
-       test -n "$force_rebase" && force_rebase="--no-ff"
-       test -n "$restrict_revision" && \
-               restrict_revision="--restrict-revision=^$restrict_revision"
-       test -n "$upstream" && upstream="--upstream=$upstream"
-       test -n "$onto" && onto="--onto=$onto"
-       test -n "$squash_onto" && squash_onto="--squash-onto=$squash_onto"
-       test -n "$onto_name" && onto_name="--onto-name=$onto_name"
-       test -n "$head_name" && head_name="--head-name=$head_name"
-       test -n "$strategy" && strategy="--strategy=$strategy"
-       test -n "$strategy_opts" && strategy_opts="--strategy-opts=$strategy_opts"
-       test -n "$switch_to" && switch_to="--switch-to=$switch_to"
-       test -n "$cmd" && cmd="--cmd=$cmd"
-       test -n "$action" && action="--$action"
-
-       exec git rebase--interactive "$action" "$keep_empty" "$rebase_merges" "$rebase_cousins" \
-               "$upstream" "$onto" "$squash_onto" "$restrict_revision" \
-               "$allow_empty_message" "$autosquash" "$verbose" \
-               "$force_rebase" "$onto_name" "$head_name" "$strategy" \
-               "$strategy_opts" "$cmd" "$switch_to" \
-               "$allow_rerere_autoupdate" "$gpg_sign_opt" "$signoff" \
-               "$reschedule_failed_exec"
-}
-
-run_specific_rebase () {
-       if [ "$interactive_rebase" = implied ]; then
-               GIT_SEQUENCE_EDITOR=:
-               export GIT_SEQUENCE_EDITOR
-               autosquash=
-       fi
-
-       if test -n "$interactive_rebase" -a -z "$preserve_merges"
-       then
-               run_interactive
-       else
-               . git-rebase--$type
-
-               if test -z "$preserve_merges"
-               then
-                       git_rebase__$type
-               else
-                       git_rebase__preserve_merges
-               fi
-       fi
-
-       ret=$?
-       if test $ret -eq 0
-       then
-               finish_rebase
-       elif test $ret -eq 2 # special exit status for rebase -p
-       then
-               apply_autostash &&
-               rm -rf "$state_dir" &&
-               die "Nothing to do"
-       fi
-       exit $ret
-}
-
-run_pre_rebase_hook () {
-       if test -z "$ok_to_skip_pre_rebase" &&
-          test -x "$(git rev-parse --git-path hooks/pre-rebase)"
-       then
-               "$(git rev-parse --git-path hooks/pre-rebase)" ${1+"$@"} ||
-               die "$(gettext "The pre-rebase hook refused to rebase.")"
-       fi
-}
-
-test -f "$apply_dir"/applying &&
-       die "$(gettext "It looks like 'git am' is in progress. Cannot rebase.")"
-
-if test -d "$apply_dir"
-then
-       type=am
-       state_dir="$apply_dir"
-elif test -d "$merge_dir"
-then
-       type=interactive
-       if test -d "$merge_dir"/rewritten
-       then
-               type=preserve-merges
-               interactive_rebase=explicit
-               preserve_merges=t
-       elif test -f "$merge_dir"/interactive
-       then
-               interactive_rebase=explicit
-       fi
-       state_dir="$merge_dir"
-fi
-test -n "$type" && in_progress=t
-
-total_argc=$#
-while test $# != 0
-do
-       case "$1" in
-       --no-verify)
-               ok_to_skip_pre_rebase=yes
-               ;;
-       --verify)
-               ok_to_skip_pre_rebase=
-               ;;
-       --continue|--skip|--abort|--quit|--edit-todo|--show-current-patch)
-               test $total_argc -eq 2 || usage
-               action=${1##--}
-               ;;
-       --onto=*)
-               onto="${1#--onto=}"
-               ;;
-       --exec=*)
-               cmd="${cmd}exec ${1#--exec=}${LF}"
-               test -z "$interactive_rebase" && interactive_rebase=implied
-               ;;
-       --interactive)
-               interactive_rebase=explicit
-               ;;
-       --keep-empty)
-               keep_empty=yes
-               ;;
-       --allow-empty-message)
-               allow_empty_message=--allow-empty-message
-               ;;
-       --no-keep-empty)
-               keep_empty=
-               ;;
-       --rebase-merges)
-               rebase_merges=t
-               test -z "$interactive_rebase" && interactive_rebase=implied
-               ;;
-       --rebase-merges=*)
-               rebase_merges=t
-               case "${1#*=}" in
-               rebase-cousins) rebase_cousins=t;;
-               no-rebase-cousins) rebase_cousins=;;
-               *) die "Unknown mode: $1";;
-               esac
-               test -z "$interactive_rebase" && interactive_rebase=implied
-               ;;
-       --preserve-merges)
-               preserve_merges=t
-               test -z "$interactive_rebase" && interactive_rebase=implied
-               ;;
-       --autosquash)
-               autosquash=t
-               ;;
-       --no-autosquash)
-               autosquash=
-               ;;
-       --fork-point)
-               fork_point=t
-               ;;
-       --no-fork-point)
-               fork_point=
-               ;;
-       --merge)
-               do_merge=t
-               ;;
-       --strategy-option=*)
-               strategy_opts="$strategy_opts $(git rev-parse --sq-quote "--${1#--strategy-option=}" | sed -e s/^.//)"
-               do_merge=t
-               test -z "$strategy" && strategy=recursive
-               ;;
-       --strategy=*)
-               strategy="${1#--strategy=}"
-               do_merge=t
-               ;;
-       --no-stat)
-               diffstat=
-               ;;
-       --stat)
-               diffstat=t
-               ;;
-       --autostash)
-               autostash=true
-               ;;
-       --no-autostash)
-               autostash=false
-               ;;
-       --verbose)
-               verbose=t
-               diffstat=t
-               GIT_QUIET=
-               ;;
-       --quiet)
-               GIT_QUIET=t
-               git_am_opt="$git_am_opt -q"
-               verbose=
-               diffstat=
-               ;;
-       --whitespace=*)
-               git_am_opt="$git_am_opt --whitespace=${1#--whitespace=}"
-               case "${1#--whitespace=}" in
-               fix|strip)
-                       force_rebase=t
-                       ;;
-               warn|nowarn|error|error-all)
-                       ;; # okay, known whitespace option
-               *)
-                       die "fatal: Invalid whitespace option: '${1#*=}'"
-                       ;;
-               esac
-               ;;
-       --ignore-whitespace)
-               git_am_opt="$git_am_opt $1"
-               ;;
-       --signoff)
-               signoff=--signoff
-               ;;
-       --no-signoff)
-               signoff=
-               ;;
-       --committer-date-is-author-date|--ignore-date)
-               git_am_opt="$git_am_opt $1"
-               force_rebase=t
-               ;;
-       -C*[!0-9]*)
-               die "fatal: switch \`C' expects a numerical value"
-               ;;
-       -C*)
-               git_am_opt="$git_am_opt $1"
-               ;;
-       --root)
-               rebase_root=t
-               ;;
-       --force-rebase|--no-ff)
-               force_rebase=t
-               ;;
-       --rerere-autoupdate|--no-rerere-autoupdate)
-               allow_rerere_autoupdate="$1"
-               ;;
-       --gpg-sign)
-               gpg_sign_opt=-S
-               ;;
-       --gpg-sign=*)
-               gpg_sign_opt="-S${1#--gpg-sign=}"
-               ;;
-       --reschedule-failed-exec)
-               reschedule_failed_exec=--reschedule-failed-exec
-               ;;
-       --no-reschedule-failed-exec)
-               reschedule_failed_exec=
-               ;;
-       --)
-               shift
-               break
-               ;;
-       *)
-               usage
-               ;;
-       esac
-       shift
-done
-test $# -gt 2 && usage
-
-if test -n "$action"
-then
-       test -z "$in_progress" && die "$(gettext "No rebase in progress?")"
-       # Only interactive rebase uses detailed reflog messages
-       if test -n "$interactive_rebase" && test "$GIT_REFLOG_ACTION" = rebase
-       then
-               GIT_REFLOG_ACTION="rebase -i ($action)"
-               export GIT_REFLOG_ACTION
-       fi
-fi
-
-if test "$action" = "edit-todo" && test -z "$interactive_rebase"
-then
-       die "$(gettext "The --edit-todo action can only be used during interactive rebase.")"
-fi
-
-case "$action" in
-continue)
-       # Sanity check
-       git rev-parse --verify HEAD >/dev/null ||
-               die "$(gettext "Cannot read HEAD")"
-       git update-index --ignore-submodules --refresh &&
-       git diff-files --quiet --ignore-submodules || {
-               echo "$(gettext "You must edit all merge conflicts and then
-mark them as resolved using git add")"
-               exit 1
-       }
-       read_basic_state
-       run_specific_rebase
-       ;;
-skip)
-       output git reset --hard HEAD || exit $?
-       read_basic_state
-       run_specific_rebase
-       ;;
-abort)
-       git rerere clear
-       read_basic_state
-       case "$head_name" in
-       refs/*)
-               git symbolic-ref -m "rebase: aborting" HEAD $head_name ||
-               die "$(eval_gettext "Could not move back to \$head_name")"
-               ;;
-       esac
-       output git reset --hard $orig_head
-       finish_rebase
-       exit
-       ;;
-quit)
-       exec rm -rf "$state_dir"
-       ;;
-edit-todo)
-       run_specific_rebase
-       ;;
-show-current-patch)
-       run_specific_rebase
-       die "BUG: run_specific_rebase is not supposed to return here"
-       ;;
-esac
-
-# Make sure no rebase is in progress
-if test -n "$in_progress"
-then
-       state_dir_base=${state_dir##*/}
-       cmd_live_rebase="git rebase (--continue | --abort | --skip)"
-       cmd_clear_stale_rebase="rm -fr \"$state_dir\""
-       die "
-$(eval_gettext 'It seems that there is already a $state_dir_base directory, and
-I wonder if you are in the middle of another rebase.  If that is the
-case, please try
-       $cmd_live_rebase
-If that is not the case, please
-       $cmd_clear_stale_rebase
-and run me again.  I am stopping in case you still have something
-valuable there.')"
-fi
-
-if test -n "$rebase_root" && test -z "$onto"
-then
-       test -z "$interactive_rebase" && interactive_rebase=implied
-fi
-
-if test -n "$keep_empty"
-then
-       test -z "$interactive_rebase" && interactive_rebase=implied
-fi
-
-actually_interactive=
-if test -n "$interactive_rebase"
-then
-       if test -z "$preserve_merges"
-       then
-               type=interactive
-       else
-               type=preserve-merges
-       fi
-       actually_interactive=t
-       state_dir="$merge_dir"
-elif test -n "$do_merge"
-then
-       interactive_rebase=implied
-       type=interactive
-       state_dir="$merge_dir"
-else
-       type=am
-       state_dir="$apply_dir"
-fi
-
-if test -t 2 && test -z "$GIT_QUIET"
-then
-       git_format_patch_opt="$git_format_patch_opt --progress"
-fi
-
-incompatible_opts=$(echo " $git_am_opt " | \
-                   sed -e 's/ -q / /g' -e 's/^ \(.*\) $/\1/')
-if test -n "$incompatible_opts"
-then
-       if test -n "$actually_interactive" || test "$do_merge"
-       then
-               die "$(gettext "fatal: cannot combine am options with either interactive or merge options")"
-       fi
-fi
-
-if test -n "$signoff"
-then
-       test -n "$preserve_merges" &&
-               die "$(gettext "fatal: cannot combine '--signoff' with '--preserve-merges'")"
-       git_am_opt="$git_am_opt $signoff"
-       force_rebase=t
-fi
-
-if test -n "$preserve_merges"
-then
-       # Note: incompatibility with --signoff handled in signoff block above
-       # Note: incompatibility with --interactive is just a strong warning;
-       #       git-rebase.txt caveats with "unless you know what you are doing"
-       test -n "$rebase_merges" &&
-               die "$(gettext "fatal: cannot combine '--preserve-merges' with '--rebase-merges'")"
-
-       test -n "$reschedule_failed_exec" &&
-               die "$(gettext "error: cannot combine '--preserve-merges' with '--reschedule-failed-exec'")"
-fi
-
-if test -n "$rebase_merges"
-then
-       test -n "$strategy_opts" &&
-               die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy-option'")"
-       test -n "$strategy" &&
-               die "$(gettext "fatal: cannot combine '--rebase-merges' with '--strategy'")"
-fi
-
-if test -z "$rebase_root"
-then
-       case "$#" in
-       0)
-               if ! upstream_name=$(git rev-parse --symbolic-full-name \
-                       --verify -q @{upstream} 2>/dev/null)
-               then
-                       . git-parse-remote
-                       error_on_missing_default_upstream "rebase" "rebase" \
-                               "against" "git rebase $(gettext '<branch>')"
-               fi
-
-               test "$fork_point" = auto && fork_point=t
-               ;;
-       *)      upstream_name="$1"
-               if test "$upstream_name" = "-"
-               then
-                       upstream_name="@{-1}"
-               fi
-               shift
-               ;;
-       esac
-       upstream=$(peel_committish "${upstream_name}") ||
-       die "$(eval_gettext "invalid upstream '\$upstream_name'")"
-       upstream_arg="$upstream_name"
-else
-       if test -z "$onto"
-       then
-               empty_tree=$(git hash-object -t tree /dev/null)
-               onto=$(git commit-tree $empty_tree </dev/null)
-               squash_onto="$onto"
-       fi
-       unset upstream_name
-       unset upstream
-       test $# -gt 1 && usage
-       upstream_arg=--root
-fi
-
-# Make sure the branch to rebase onto is valid.
-onto_name=${onto-"$upstream_name"}
-case "$onto_name" in
-*...*)
-       if      left=${onto_name%...*} right=${onto_name#*...} &&
-               onto=$(git merge-base --all ${left:-HEAD} ${right:-HEAD})
-       then
-               case "$onto" in
-               ?*"$LF"?*)
-                       die "$(eval_gettext "\$onto_name: there are more than one merge bases")"
-                       ;;
-               '')
-                       die "$(eval_gettext "\$onto_name: there is no merge base")"
-                       ;;
-               esac
-       else
-               die "$(eval_gettext "\$onto_name: there is no merge base")"
-       fi
-       ;;
-*)
-       onto=$(peel_committish "$onto_name") ||
-       die "$(eval_gettext "Does not point to a valid commit: \$onto_name")"
-       ;;
-esac
-
-# If the branch to rebase is given, that is the branch we will rebase
-# $branch_name -- branch/commit being rebased, or HEAD (already detached)
-# $orig_head -- commit object name of tip of the branch before rebasing
-# $head_name -- refs/heads/<that-branch> or "detached HEAD"
-switch_to=
-case "$#" in
-1)
-       # Is it "rebase other $branchname" or "rebase other $commit"?
-       branch_name="$1"
-       switch_to="$1"
-
-       # Is it a local branch?
-       if git show-ref --verify --quiet -- "refs/heads/$branch_name" &&
-          orig_head=$(git rev-parse -q --verify "refs/heads/$branch_name")
-       then
-               head_name="refs/heads/$branch_name"
-       # If not is it a valid ref (branch or commit)?
-       elif orig_head=$(git rev-parse -q --verify "$branch_name")
-       then
-               head_name="detached HEAD"
-
-       else
-               die "$(eval_gettext "fatal: no such branch/commit '\$branch_name'")"
-       fi
-       ;;
-0)
-       # Do not need to switch branches, we are already on it.
-       if branch_name=$(git symbolic-ref -q HEAD)
-       then
-               head_name=$branch_name
-               branch_name=$(expr "z$branch_name" : 'zrefs/heads/\(.*\)')
-       else
-               head_name="detached HEAD"
-               branch_name=HEAD
-       fi
-       orig_head=$(git rev-parse --verify HEAD) || exit
-       ;;
-*)
-       die "BUG: unexpected number of arguments left to parse"
-       ;;
-esac
-
-if test "$fork_point" = t
-then
-       new_upstream=$(git merge-base --fork-point "$upstream_name" \
-                       "${switch_to:-HEAD}")
-       if test -n "$new_upstream"
-       then
-               restrict_revision=$new_upstream
-       fi
-fi
-
-if test "$autostash" = true && ! (require_clean_work_tree) 2>/dev/null
-then
-       stash_sha1=$(git stash create "autostash") ||
-       die "$(gettext 'Cannot autostash')"
-
-       mkdir -p "$state_dir" &&
-       echo $stash_sha1 >"$state_dir/autostash" &&
-       stash_abbrev=$(git rev-parse --short $stash_sha1) &&
-       echo "$(eval_gettext 'Created autostash: $stash_abbrev')" &&
-       git reset --hard
-fi
-
-require_clean_work_tree "rebase" "$(gettext "Please commit or stash them.")"
-
-# Now we are rebasing commits $upstream..$orig_head (or with --root,
-# everything leading up to $orig_head) on top of $onto
-
-# Check if we are already based on $onto with linear history,
-# but this should be done only when upstream and onto are the same
-# and if this is not an interactive rebase.
-mb=$(git merge-base "$onto" "$orig_head")
-if test -z "$actually_interactive" && test "$upstream" = "$onto" &&
-       test "$mb" = "$onto" && test -z "$restrict_revision" &&
-       # linear history?
-       ! (git rev-list --parents "$onto".."$orig_head" | sane_grep " .* ") > /dev/null
-then
-       if test -z "$force_rebase"
-       then
-               # Lazily switch to the target branch if needed...
-               test -z "$switch_to" ||
-               GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $switch_to" \
-                       git checkout -q "$switch_to" --
-               if test "$branch_name" = "HEAD" &&
-                        ! git symbolic-ref -q HEAD
-               then
-                       say "$(eval_gettext "HEAD is up to date.")"
-               else
-                       say "$(eval_gettext "Current branch \$branch_name is up to date.")"
-               fi
-               finish_rebase
-               exit 0
-       else
-               if test "$branch_name" = "HEAD" &&
-                        ! git symbolic-ref -q HEAD
-               then
-                       say "$(eval_gettext "HEAD is up to date, rebase forced.")"
-               else
-                       say "$(eval_gettext "Current branch \$branch_name is up to date, rebase forced.")"
-               fi
-       fi
-fi
-
-# If a hook exists, give it a chance to interrupt
-run_pre_rebase_hook "$upstream_arg" "$@"
-
-if test -n "$diffstat"
-then
-       if test -n "$verbose"
-       then
-               if test -z "$mb"
-               then
-                       echo "$(eval_gettext "Changes to \$onto:")"
-               else
-                       echo "$(eval_gettext "Changes from \$mb to \$onto:")"
-               fi
-       fi
-       mb_tree="${mb:-$(git hash-object -t tree /dev/null)}"
-       # We want color (if set), but no pager
-       GIT_PAGER='' git diff --stat --summary "$mb_tree" "$onto"
-fi
-
-if test -z "$actually_interactive" && test "$mb" = "$orig_head"
-then
-       say "$(eval_gettext "Fast-forwarded \$branch_name to \$onto_name.")"
-       GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
-               git checkout -q "$onto^0" || die "could not detach HEAD"
-       # If the $onto is a proper descendant of the tip of the branch, then
-       # we just fast-forwarded.
-       git update-ref ORIG_HEAD $orig_head
-       move_to_original_branch
-       finish_rebase
-       exit 0
-fi
-
-test -n "$interactive_rebase" && run_specific_rebase
-
-# Detach HEAD and reset the tree
-say "$(gettext "First, rewinding head to replay your work on top of it...")"
-
-GIT_REFLOG_ACTION="$GIT_REFLOG_ACTION: checkout $onto_name" \
-       git checkout -q "$onto^0" || die "could not detach HEAD"
-git update-ref ORIG_HEAD $orig_head
-
-if test -n "$rebase_root"
-then
-       revisions="$onto..$orig_head"
-else
-       revisions="${restrict_revision-$upstream}..$orig_head"
-fi
-
-run_specific_rebase
diff --git a/git-legacy-stash.sh b/git-legacy-stash.sh
new file mode 100755 (executable)
index 0000000..f60e9b3
--- /dev/null
@@ -0,0 +1,797 @@
+#!/bin/sh
+# Copyright (c) 2007, Nanako Shiraishi
+
+dashless=$(basename "$0" | sed -e 's/-/ /')
+USAGE="list [<options>]
+   or: $dashless show [<stash>]
+   or: $dashless drop [-q|--quiet] [<stash>]
+   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
+   or: $dashless branch <branchname> [<stash>]
+   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+                     [-u|--include-untracked] [-a|--all] [<message>]
+   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+                      [-u|--include-untracked] [-a|--all] [-m <message>]
+                      [-- <pathspec>...]]
+   or: $dashless clear"
+
+SUBDIRECTORY_OK=Yes
+OPTIONS_SPEC=
+START_DIR=$(pwd)
+. git-sh-setup
+require_work_tree
+prefix=$(git rev-parse --show-prefix) || exit 1
+cd_to_toplevel
+
+TMP="$GIT_DIR/.git-stash.$$"
+TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
+trap 'rm -f "$TMP-"* "$TMPindex"' 0
+
+ref_stash=refs/stash
+
+if git config --get-colorbool color.interactive; then
+       help_color="$(git config --get-color color.interactive.help 'red bold')"
+       reset_color="$(git config --get-color '' reset)"
+else
+       help_color=
+       reset_color=
+fi
+
+no_changes () {
+       git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
+       git diff-files --quiet --ignore-submodules -- "$@" &&
+       (test -z "$untracked" || test -z "$(untracked_files "$@")")
+}
+
+untracked_files () {
+       if test "$1" = "-z"
+       then
+               shift
+               z=-z
+       else
+               z=
+       fi
+       excl_opt=--exclude-standard
+       test "$untracked" = "all" && excl_opt=
+       git ls-files -o $z $excl_opt -- "$@"
+}
+
+prepare_fallback_ident () {
+       if ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1
+       then
+               GIT_AUTHOR_NAME="git stash"
+               GIT_AUTHOR_EMAIL=git@stash
+               GIT_COMMITTER_NAME="git stash"
+               GIT_COMMITTER_EMAIL=git@stash
+               export GIT_AUTHOR_NAME
+               export GIT_AUTHOR_EMAIL
+               export GIT_COMMITTER_NAME
+               export GIT_COMMITTER_EMAIL
+       fi
+}
+
+clear_stash () {
+       if test $# != 0
+       then
+               die "$(gettext "git stash clear with parameters is unimplemented")"
+       fi
+       if current=$(git rev-parse --verify --quiet $ref_stash)
+       then
+               git update-ref -d $ref_stash $current
+       fi
+}
+
+maybe_quiet () {
+       case "$1" in
+       --keep-stdout)
+               shift
+               if test -n "$GIT_QUIET"
+               then
+                       "$@" 2>/dev/null
+               else
+                       "$@"
+               fi
+               ;;
+       *)
+               if test -n "$GIT_QUIET"
+               then
+                       "$@" >/dev/null 2>&1
+               else
+                       "$@"
+               fi
+               ;;
+       esac
+}
+
+create_stash () {
+
+       prepare_fallback_ident
+
+       stash_msg=
+       untracked=
+       while test $# != 0
+       do
+               case "$1" in
+               -m|--message)
+                       shift
+                       stash_msg=${1?"BUG: create_stash () -m requires an argument"}
+                       ;;
+               -m*)
+                       stash_msg=${1#-m}
+                       ;;
+               --message=*)
+                       stash_msg=${1#--message=}
+                       ;;
+               -u|--include-untracked)
+                       shift
+                       untracked=${1?"BUG: create_stash () -u requires an argument"}
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               esac
+               shift
+       done
+
+       git update-index -q --refresh
+       if maybe_quiet no_changes "$@"
+       then
+               exit 0
+       fi
+
+       # state of the base commit
+       if b_commit=$(maybe_quiet --keep-stdout git rev-parse --verify HEAD)
+       then
+               head=$(git rev-list --oneline -n 1 HEAD --)
+       elif test -n "$GIT_QUIET"
+       then
+               exit 1
+       else
+               die "$(gettext "You do not have the initial commit yet")"
+       fi
+
+       if branch=$(git symbolic-ref -q HEAD)
+       then
+               branch=${branch#refs/heads/}
+       else
+               branch='(no branch)'
+       fi
+       msg=$(printf '%s: %s' "$branch" "$head")
+
+       # state of the index
+       i_tree=$(git write-tree) &&
+       i_commit=$(printf 'index on %s\n' "$msg" |
+               git commit-tree $i_tree -p $b_commit) ||
+               die "$(gettext "Cannot save the current index state")"
+
+       if test -n "$untracked"
+       then
+               # Untracked files are stored by themselves in a parentless commit, for
+               # ease of unpacking later.
+               u_commit=$(
+                       untracked_files -z "$@" | (
+                               GIT_INDEX_FILE="$TMPindex" &&
+                               export GIT_INDEX_FILE &&
+                               rm -f "$TMPindex" &&
+                               git update-index -z --add --remove --stdin &&
+                               u_tree=$(git write-tree) &&
+                               printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree  &&
+                               rm -f "$TMPindex"
+               ) ) || die "$(gettext "Cannot save the untracked files")"
+
+               untracked_commit_option="-p $u_commit";
+       else
+               untracked_commit_option=
+       fi
+
+       if test -z "$patch_mode"
+       then
+
+               # state of the working tree
+               w_tree=$( (
+                       git read-tree --index-output="$TMPindex" -m $i_tree &&
+                       GIT_INDEX_FILE="$TMPindex" &&
+                       export GIT_INDEX_FILE &&
+                       git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
+                       git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
+                       git write-tree &&
+                       rm -f "$TMPindex"
+               ) ) ||
+                       die "$(gettext "Cannot save the current worktree state")"
+
+       else
+
+               rm -f "$TMP-index" &&
+               GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
+
+               # find out what the user wants
+               GIT_INDEX_FILE="$TMP-index" \
+                       git add--interactive --patch=stash -- "$@" &&
+
+               # state of the working tree
+               w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
+               die "$(gettext "Cannot save the current worktree state")"
+
+               git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
+               test -s "$TMP-patch" ||
+               die "$(gettext "No changes selected")"
+
+               rm -f "$TMP-index" ||
+               die "$(gettext "Cannot remove temporary index (can't happen)")"
+
+       fi
+
+       # create the stash
+       if test -z "$stash_msg"
+       then
+               stash_msg=$(printf 'WIP on %s' "$msg")
+       else
+               stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
+       fi
+       w_commit=$(printf '%s\n' "$stash_msg" |
+       git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
+       die "$(gettext "Cannot record working tree state")"
+}
+
+store_stash () {
+       while test $# != 0
+       do
+               case "$1" in
+               -m|--message)
+                       shift
+                       stash_msg="$1"
+                       ;;
+               -m*)
+                       stash_msg=${1#-m}
+                       ;;
+               --message=*)
+                       stash_msg=${1#--message=}
+                       ;;
+               -q|--quiet)
+                       quiet=t
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+       test $# = 1 ||
+       die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
+
+       w_commit="$1"
+       if test -z "$stash_msg"
+       then
+               stash_msg="Created via \"git stash store\"."
+       fi
+
+       git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
+       ret=$?
+       test $ret != 0 && test -z "$quiet" &&
+       die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
+       return $ret
+}
+
+push_stash () {
+       keep_index=
+       patch_mode=
+       untracked=
+       stash_msg=
+       while test $# != 0
+       do
+               case "$1" in
+               -k|--keep-index)
+                       keep_index=t
+                       ;;
+               --no-keep-index)
+                       keep_index=n
+                       ;;
+               -p|--patch)
+                       patch_mode=t
+                       # only default to keep if we don't already have an override
+                       test -z "$keep_index" && keep_index=t
+                       ;;
+               -q|--quiet)
+                       GIT_QUIET=t
+                       ;;
+               -u|--include-untracked)
+                       untracked=untracked
+                       ;;
+               -a|--all)
+                       untracked=all
+                       ;;
+               -m|--message)
+                       shift
+                       test -z ${1+x} && usage
+                       stash_msg=$1
+                       ;;
+               -m*)
+                       stash_msg=${1#-m}
+                       ;;
+               --message=*)
+                       stash_msg=${1#--message=}
+                       ;;
+               --help)
+                       show_help
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       option="$1"
+                       eval_gettextln "error: unknown option for 'stash push': \$option"
+                       usage
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
+       eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
+
+       if test -n "$patch_mode" && test -n "$untracked"
+       then
+               die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
+       fi
+
+       test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
+
+       git update-index -q --refresh
+       if maybe_quiet no_changes "$@"
+       then
+               say "$(gettext "No local changes to save")"
+               exit 0
+       fi
+
+       git reflog exists $ref_stash ||
+               clear_stash || die "$(gettext "Cannot initialize stash")"
+
+       create_stash -m "$stash_msg" -u "$untracked" -- "$@"
+       store_stash -m "$stash_msg" -q $w_commit ||
+       die "$(gettext "Cannot save the current status")"
+       say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
+
+       if test -z "$patch_mode"
+       then
+               test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
+               if test -n "$untracked" && test $# = 0
+               then
+                       git clean --force --quiet -d $CLEAN_X_OPTION
+               fi
+
+               if test $# != 0
+               then
+                       test -z "$untracked" && UPDATE_OPTION="-u" || UPDATE_OPTION=
+                       test "$untracked" = "all" && FORCE_OPTION="--force" || FORCE_OPTION=
+                       git add $UPDATE_OPTION $FORCE_OPTION -- "$@"
+                       git diff-index -p --cached --binary HEAD -- "$@" |
+                       git apply --index -R
+               else
+                       git reset --hard -q
+               fi
+
+               if test "$keep_index" = "t" && test -n "$i_tree"
+               then
+                       git read-tree --reset $i_tree
+                       git ls-files -z --modified -- "$@" |
+                       git checkout-index -z --force --stdin
+               fi
+       else
+               git apply -R < "$TMP-patch" ||
+               die "$(gettext "Cannot remove worktree changes")"
+
+               if test "$keep_index" != "t"
+               then
+                       git reset -q -- "$@"
+               fi
+       fi
+}
+
+save_stash () {
+       push_options=
+       while test $# != 0
+       do
+               case "$1" in
+               -q|--quiet)
+                       GIT_QUIET=t
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       # pass all options through to push_stash
+                       push_options="$push_options $1"
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
+       stash_msg="$*"
+
+       if test -z "$stash_msg"
+       then
+               push_stash $push_options
+       else
+               push_stash $push_options -m "$stash_msg"
+       fi
+}
+
+have_stash () {
+       git rev-parse --verify --quiet $ref_stash >/dev/null
+}
+
+list_stash () {
+       have_stash || return 0
+       git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash --
+}
+
+show_stash () {
+       ALLOW_UNKNOWN_FLAGS=t
+       assert_stash_like "$@"
+
+       if test -z "$FLAGS"
+       then
+               if test "$(git config --bool stash.showStat || echo true)" = "true"
+               then
+                       FLAGS=--stat
+               fi
+
+               if test "$(git config --bool stash.showPatch || echo false)" = "true"
+               then
+                       FLAGS=${FLAGS}${FLAGS:+ }-p
+               fi
+
+               if test -z "$FLAGS"
+               then
+                       return 0
+               fi
+       fi
+
+       git diff ${FLAGS} $b_commit $w_commit
+}
+
+show_help () {
+       exec git help stash
+       exit 1
+}
+
+#
+# Parses the remaining options looking for flags and
+# at most one revision defaulting to ${ref_stash}@{0}
+# if none found.
+#
+# Derives related tree and commit objects from the
+# revision, if one is found.
+#
+# stash records the work tree, and is a merge between the
+# base commit (first parent) and the index tree (second parent).
+#
+#   REV is set to the symbolic version of the specified stash-like commit
+#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
+#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
+#   s is set to the SHA1 of the stash commit
+#   w_commit is set to the commit containing the working tree
+#   b_commit is set to the base commit
+#   i_commit is set to the commit containing the index tree
+#   u_commit is set to the commit containing the untracked files tree
+#   w_tree is set to the working tree
+#   b_tree is set to the base tree
+#   i_tree is set to the index tree
+#   u_tree is set to the untracked files tree
+#
+#   GIT_QUIET is set to t if -q is specified
+#   INDEX_OPTION is set to --index if --index is specified.
+#   FLAGS is set to the remaining flags (if allowed)
+#
+# dies if:
+#   * too many revisions specified
+#   * no revision is specified and there is no stash stack
+#   * a revision is specified which cannot be resolve to a SHA1
+#   * a non-existent stash reference is specified
+#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
+#
+
+parse_flags_and_rev()
+{
+       test "$PARSE_CACHE" = "$*" && return 0 # optimisation
+       PARSE_CACHE="$*"
+
+       IS_STASH_LIKE=
+       IS_STASH_REF=
+       INDEX_OPTION=
+       s=
+       w_commit=
+       b_commit=
+       i_commit=
+       u_commit=
+       w_tree=
+       b_tree=
+       i_tree=
+       u_tree=
+
+       FLAGS=
+       REV=
+       for opt
+       do
+               case "$opt" in
+                       -q|--quiet)
+                               GIT_QUIET=-t
+                       ;;
+                       --index)
+                               INDEX_OPTION=--index
+                       ;;
+                       --help)
+                               show_help
+                       ;;
+                       -*)
+                               test "$ALLOW_UNKNOWN_FLAGS" = t ||
+                                       die "$(eval_gettext "unknown option: \$opt")"
+                               FLAGS="${FLAGS}${FLAGS:+ }$opt"
+                       ;;
+                       *)
+                               REV="${REV}${REV:+ }'$opt'"
+                       ;;
+               esac
+       done
+
+       eval set -- $REV
+
+       case $# in
+               0)
+                       have_stash || die "$(gettext "No stash entries found.")"
+                       set -- ${ref_stash}@{0}
+               ;;
+               1)
+                       :
+               ;;
+               *)
+                       die "$(eval_gettext "Too many revisions specified: \$REV")"
+               ;;
+       esac
+
+       case "$1" in
+               *[!0-9]*)
+                       :
+               ;;
+               *)
+                       set -- "${ref_stash}@{$1}"
+               ;;
+       esac
+
+       REV=$(git rev-parse --symbolic --verify --quiet "$1") || {
+               reference="$1"
+               die "$(eval_gettext "\$reference is not a valid reference")"
+       }
+
+       i_commit=$(git rev-parse --verify --quiet "$REV^2") &&
+       set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
+       s=$1 &&
+       w_commit=$1 &&
+       b_commit=$2 &&
+       w_tree=$3 &&
+       b_tree=$4 &&
+       i_tree=$5 &&
+       IS_STASH_LIKE=t &&
+       test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
+       IS_STASH_REF=t
+
+       u_commit=$(git rev-parse --verify --quiet "$REV^3") &&
+       u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
+}
+
+is_stash_like()
+{
+       parse_flags_and_rev "$@"
+       test -n "$IS_STASH_LIKE"
+}
+
+assert_stash_like() {
+       is_stash_like "$@" || {
+               args="$*"
+               die "$(eval_gettext "'\$args' is not a stash-like commit")"
+       }
+}
+
+is_stash_ref() {
+       is_stash_like "$@" && test -n "$IS_STASH_REF"
+}
+
+assert_stash_ref() {
+       is_stash_ref "$@" || {
+               args="$*"
+               die "$(eval_gettext "'\$args' is not a stash reference")"
+       }
+}
+
+apply_stash () {
+
+       assert_stash_like "$@"
+
+       git update-index -q --refresh || die "$(gettext "unable to refresh index")"
+
+       # current index state
+       c_tree=$(git write-tree) ||
+               die "$(gettext "Cannot apply a stash in the middle of a merge")"
+
+       unstashed_index_tree=
+       if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
+                       test "$c_tree" != "$i_tree"
+       then
+               git diff-tree --binary $s^2^..$s^2 | git apply --cached
+               test $? -ne 0 &&
+                       die "$(gettext "Conflicts in index. Try without --index.")"
+               unstashed_index_tree=$(git write-tree) ||
+                       die "$(gettext "Could not save index tree")"
+               git reset
+       fi
+
+       if test -n "$u_tree"
+       then
+               GIT_INDEX_FILE="$TMPindex" git read-tree "$u_tree" &&
+               GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
+               rm -f "$TMPindex" ||
+               die "$(gettext "Could not restore untracked files from stash entry")"
+       fi
+
+       eval "
+               GITHEAD_$w_tree='Stashed changes' &&
+               GITHEAD_$c_tree='Updated upstream' &&
+               GITHEAD_$b_tree='Version stash was based on' &&
+               export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
+       "
+
+       if test -n "$GIT_QUIET"
+       then
+               GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
+       fi
+       if git merge-recursive $b_tree -- $c_tree $w_tree
+       then
+               # No conflict
+               if test -n "$unstashed_index_tree"
+               then
+                       git read-tree "$unstashed_index_tree"
+               else
+                       a="$TMP-added" &&
+                       git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
+                       git read-tree --reset $c_tree &&
+                       git update-index --add --stdin <"$a" ||
+                               die "$(gettext "Cannot unstage modified files")"
+                       rm -f "$a"
+               fi
+               squelch=
+               if test -n "$GIT_QUIET"
+               then
+                       squelch='>/dev/null 2>&1'
+               fi
+               (cd "$START_DIR" && eval "git status $squelch") || :
+       else
+               # Merge conflict; keep the exit status from merge-recursive
+               status=$?
+               git rerere
+               if test -n "$INDEX_OPTION"
+               then
+                       gettextln "Index was not unstashed." >&2
+               fi
+               exit $status
+       fi
+}
+
+pop_stash() {
+       assert_stash_ref "$@"
+
+       if apply_stash "$@"
+       then
+               drop_stash "$@"
+       else
+               status=$?
+               say "$(gettext "The stash entry is kept in case you need it again.")"
+               exit $status
+       fi
+}
+
+drop_stash () {
+       assert_stash_ref "$@"
+
+       git reflog delete --updateref --rewrite "${REV}" &&
+               say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
+               die "$(eval_gettext "\${REV}: Could not drop stash entry")"
+
+       # clear_stash if we just dropped the last stash entry
+       git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
+       clear_stash
+}
+
+apply_to_branch () {
+       test -n "$1" || die "$(gettext "No branch name specified")"
+       branch=$1
+       shift 1
+
+       set -- --index "$@"
+       assert_stash_like "$@"
+
+       git checkout -b $branch $REV^ &&
+       apply_stash "$@" && {
+               test -z "$IS_STASH_REF" || drop_stash "$@"
+       }
+}
+
+test "$1" = "-p" && set "push" "$@"
+
+PARSE_CACHE='--not-parsed'
+# The default command is "push" if nothing but options are given
+seen_non_option=
+for opt
+do
+       case "$opt" in
+       --) break ;;
+       -*) ;;
+       *) seen_non_option=t; break ;;
+       esac
+done
+
+test -n "$seen_non_option" || set "push" "$@"
+
+# Main command set
+case "$1" in
+list)
+       shift
+       list_stash "$@"
+       ;;
+show)
+       shift
+       show_stash "$@"
+       ;;
+save)
+       shift
+       save_stash "$@"
+       ;;
+push)
+       shift
+       push_stash "$@"
+       ;;
+apply)
+       shift
+       apply_stash "$@"
+       ;;
+clear)
+       shift
+       clear_stash "$@"
+       ;;
+create)
+       shift
+       create_stash -m "$*" && echo "$w_commit"
+       ;;
+store)
+       shift
+       store_stash "$@"
+       ;;
+drop)
+       shift
+       drop_stash "$@"
+       ;;
+pop)
+       shift
+       pop_stash "$@"
+       ;;
+branch)
+       shift
+       apply_to_branch "$@"
+       ;;
+*)
+       case $# in
+       0)
+               push_stash &&
+               say "$(gettext "(To restore them type \"git stash apply\")")"
+               ;;
+       *)
+               usage
+       esac
+       ;;
+esac
index 83bf52494cd26437eaa3c755c3359951937f1ab2..aaa4eed0bc7fc2c5706853d39e4644c9d31e1e0d 100644 (file)
@@ -279,6 +279,7 @@ list_merge_tool_candidates () {
                fi
                tools="$tools gvimdiff diffuse diffmerge ecmerge"
                tools="$tools p4merge araxis bc codecompare"
+               tools="$tools smerge"
        fi
        case "${VISUAL:-$EDITOR}" in
        *vim*)
index 378928518b2c42ee3adb69b52d3b0eb63fca8ac3..10d976418568556e83b47df89905a6a95a7d8915 100644 (file)
@@ -101,6 +101,7 @@ $LONG_USAGE")"
        case "$1" in
                -h)
                echo "$LONG_USAGE"
+               case "$0" in *git-legacy-stash) exit 129;; esac
                exit
        esac
 fi
diff --git a/git-stash.sh b/git-stash.sh
deleted file mode 100755 (executable)
index 789ce2f..0000000
+++ /dev/null
@@ -1,769 +0,0 @@
-#!/bin/sh
-# Copyright (c) 2007, Nanako Shiraishi
-
-dashless=$(basename "$0" | sed -e 's/-/ /')
-USAGE="list [<options>]
-   or: $dashless show [<stash>]
-   or: $dashless drop [-q|--quiet] [<stash>]
-   or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
-   or: $dashless branch <branchname> [<stash>]
-   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
-                     [-u|--include-untracked] [-a|--all] [<message>]
-   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
-                      [-u|--include-untracked] [-a|--all] [-m <message>]
-                      [-- <pathspec>...]]
-   or: $dashless clear"
-
-SUBDIRECTORY_OK=Yes
-OPTIONS_SPEC=
-START_DIR=$(pwd)
-. git-sh-setup
-require_work_tree
-prefix=$(git rev-parse --show-prefix) || exit 1
-cd_to_toplevel
-
-TMP="$GIT_DIR/.git-stash.$$"
-TMPindex=${GIT_INDEX_FILE-"$(git rev-parse --git-path index)"}.stash.$$
-trap 'rm -f "$TMP-"* "$TMPindex"' 0
-
-ref_stash=refs/stash
-
-if git config --get-colorbool color.interactive; then
-       help_color="$(git config --get-color color.interactive.help 'red bold')"
-       reset_color="$(git config --get-color '' reset)"
-else
-       help_color=
-       reset_color=
-fi
-
-no_changes () {
-       git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
-       git diff-files --quiet --ignore-submodules -- "$@" &&
-       (test -z "$untracked" || test -z "$(untracked_files "$@")")
-}
-
-untracked_files () {
-       if test "$1" = "-z"
-       then
-               shift
-               z=-z
-       else
-               z=
-       fi
-       excl_opt=--exclude-standard
-       test "$untracked" = "all" && excl_opt=
-       git ls-files -o $z $excl_opt -- "$@"
-}
-
-prepare_fallback_ident () {
-       if ! git -c user.useconfigonly=yes var GIT_COMMITTER_IDENT >/dev/null 2>&1
-       then
-               GIT_AUTHOR_NAME="git stash"
-               GIT_AUTHOR_EMAIL=git@stash
-               GIT_COMMITTER_NAME="git stash"
-               GIT_COMMITTER_EMAIL=git@stash
-               export GIT_AUTHOR_NAME
-               export GIT_AUTHOR_EMAIL
-               export GIT_COMMITTER_NAME
-               export GIT_COMMITTER_EMAIL
-       fi
-}
-
-clear_stash () {
-       if test $# != 0
-       then
-               die "$(gettext "git stash clear with parameters is unimplemented")"
-       fi
-       if current=$(git rev-parse --verify --quiet $ref_stash)
-       then
-               git update-ref -d $ref_stash $current
-       fi
-}
-
-create_stash () {
-
-       prepare_fallback_ident
-
-       stash_msg=
-       untracked=
-       while test $# != 0
-       do
-               case "$1" in
-               -m|--message)
-                       shift
-                       stash_msg=${1?"BUG: create_stash () -m requires an argument"}
-                       ;;
-               -m*)
-                       stash_msg=${1#-m}
-                       ;;
-               --message=*)
-                       stash_msg=${1#--message=}
-                       ;;
-               -u|--include-untracked)
-                       shift
-                       untracked=${1?"BUG: create_stash () -u requires an argument"}
-                       ;;
-               --)
-                       shift
-                       break
-                       ;;
-               esac
-               shift
-       done
-
-       git update-index -q --refresh
-       if no_changes "$@"
-       then
-               exit 0
-       fi
-
-       # state of the base commit
-       if b_commit=$(git rev-parse --verify HEAD)
-       then
-               head=$(git rev-list --oneline -n 1 HEAD --)
-       else
-               die "$(gettext "You do not have the initial commit yet")"
-       fi
-
-       if branch=$(git symbolic-ref -q HEAD)
-       then
-               branch=${branch#refs/heads/}
-       else
-               branch='(no branch)'
-       fi
-       msg=$(printf '%s: %s' "$branch" "$head")
-
-       # state of the index
-       i_tree=$(git write-tree) &&
-       i_commit=$(printf 'index on %s\n' "$msg" |
-               git commit-tree $i_tree -p $b_commit) ||
-               die "$(gettext "Cannot save the current index state")"
-
-       if test -n "$untracked"
-       then
-               # Untracked files are stored by themselves in a parentless commit, for
-               # ease of unpacking later.
-               u_commit=$(
-                       untracked_files -z "$@" | (
-                               GIT_INDEX_FILE="$TMPindex" &&
-                               export GIT_INDEX_FILE &&
-                               rm -f "$TMPindex" &&
-                               git update-index -z --add --remove --stdin &&
-                               u_tree=$(git write-tree) &&
-                               printf 'untracked files on %s\n' "$msg" | git commit-tree $u_tree  &&
-                               rm -f "$TMPindex"
-               ) ) || die "$(gettext "Cannot save the untracked files")"
-
-               untracked_commit_option="-p $u_commit";
-       else
-               untracked_commit_option=
-       fi
-
-       if test -z "$patch_mode"
-       then
-
-               # state of the working tree
-               w_tree=$( (
-                       git read-tree --index-output="$TMPindex" -m $i_tree &&
-                       GIT_INDEX_FILE="$TMPindex" &&
-                       export GIT_INDEX_FILE &&
-                       git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
-                       git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
-                       git write-tree &&
-                       rm -f "$TMPindex"
-               ) ) ||
-                       die "$(gettext "Cannot save the current worktree state")"
-
-       else
-
-               rm -f "$TMP-index" &&
-               GIT_INDEX_FILE="$TMP-index" git read-tree HEAD &&
-
-               # find out what the user wants
-               GIT_INDEX_FILE="$TMP-index" \
-                       git add--interactive --patch=stash -- "$@" &&
-
-               # state of the working tree
-               w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
-               die "$(gettext "Cannot save the current worktree state")"
-
-               git diff-tree -p HEAD $w_tree -- >"$TMP-patch" &&
-               test -s "$TMP-patch" ||
-               die "$(gettext "No changes selected")"
-
-               rm -f "$TMP-index" ||
-               die "$(gettext "Cannot remove temporary index (can't happen)")"
-
-       fi
-
-       # create the stash
-       if test -z "$stash_msg"
-       then
-               stash_msg=$(printf 'WIP on %s' "$msg")
-       else
-               stash_msg=$(printf 'On %s: %s' "$branch" "$stash_msg")
-       fi
-       w_commit=$(printf '%s\n' "$stash_msg" |
-       git commit-tree $w_tree -p $b_commit -p $i_commit $untracked_commit_option) ||
-       die "$(gettext "Cannot record working tree state")"
-}
-
-store_stash () {
-       while test $# != 0
-       do
-               case "$1" in
-               -m|--message)
-                       shift
-                       stash_msg="$1"
-                       ;;
-               -m*)
-                       stash_msg=${1#-m}
-                       ;;
-               --message=*)
-                       stash_msg=${1#--message=}
-                       ;;
-               -q|--quiet)
-                       quiet=t
-                       ;;
-               *)
-                       break
-                       ;;
-               esac
-               shift
-       done
-       test $# = 1 ||
-       die "$(eval_gettext "\"$dashless store\" requires one <commit> argument")"
-
-       w_commit="$1"
-       if test -z "$stash_msg"
-       then
-               stash_msg="Created via \"git stash store\"."
-       fi
-
-       git update-ref --create-reflog -m "$stash_msg" $ref_stash $w_commit
-       ret=$?
-       test $ret != 0 && test -z "$quiet" &&
-       die "$(eval_gettext "Cannot update \$ref_stash with \$w_commit")"
-       return $ret
-}
-
-push_stash () {
-       keep_index=
-       patch_mode=
-       untracked=
-       stash_msg=
-       while test $# != 0
-       do
-               case "$1" in
-               -k|--keep-index)
-                       keep_index=t
-                       ;;
-               --no-keep-index)
-                       keep_index=n
-                       ;;
-               -p|--patch)
-                       patch_mode=t
-                       # only default to keep if we don't already have an override
-                       test -z "$keep_index" && keep_index=t
-                       ;;
-               -q|--quiet)
-                       GIT_QUIET=t
-                       ;;
-               -u|--include-untracked)
-                       untracked=untracked
-                       ;;
-               -a|--all)
-                       untracked=all
-                       ;;
-               -m|--message)
-                       shift
-                       test -z ${1+x} && usage
-                       stash_msg=$1
-                       ;;
-               -m*)
-                       stash_msg=${1#-m}
-                       ;;
-               --message=*)
-                       stash_msg=${1#--message=}
-                       ;;
-               --help)
-                       show_help
-                       ;;
-               --)
-                       shift
-                       break
-                       ;;
-               -*)
-                       option="$1"
-                       eval_gettextln "error: unknown option for 'stash push': \$option"
-                       usage
-                       ;;
-               *)
-                       break
-                       ;;
-               esac
-               shift
-       done
-
-       eval "set $(git rev-parse --sq --prefix "$prefix" -- "$@")"
-
-       if test -n "$patch_mode" && test -n "$untracked"
-       then
-               die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
-       fi
-
-       test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
-
-       git update-index -q --refresh
-       if no_changes "$@"
-       then
-               say "$(gettext "No local changes to save")"
-               exit 0
-       fi
-
-       git reflog exists $ref_stash ||
-               clear_stash || die "$(gettext "Cannot initialize stash")"
-
-       create_stash -m "$stash_msg" -u "$untracked" -- "$@"
-       store_stash -m "$stash_msg" -q $w_commit ||
-       die "$(gettext "Cannot save the current status")"
-       say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
-
-       if test -z "$patch_mode"
-       then
-               test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
-               if test -n "$untracked" && test $# = 0
-               then
-                       git clean --force --quiet -d $CLEAN_X_OPTION
-               fi
-
-               if test $# != 0
-               then
-                       test -z "$untracked" && UPDATE_OPTION="-u" || UPDATE_OPTION=
-                       test "$untracked" = "all" && FORCE_OPTION="--force" || FORCE_OPTION=
-                       git add $UPDATE_OPTION $FORCE_OPTION -- "$@"
-                       git diff-index -p --cached --binary HEAD -- "$@" |
-                       git apply --index -R
-               else
-                       git reset --hard -q
-               fi
-
-               if test "$keep_index" = "t" && test -n "$i_tree"
-               then
-                       git read-tree --reset $i_tree
-                       git ls-files -z --modified -- "$@" |
-                       git checkout-index -z --force --stdin
-               fi
-       else
-               git apply -R < "$TMP-patch" ||
-               die "$(gettext "Cannot remove worktree changes")"
-
-               if test "$keep_index" != "t"
-               then
-                       git reset -q -- "$@"
-               fi
-       fi
-}
-
-save_stash () {
-       push_options=
-       while test $# != 0
-       do
-               case "$1" in
-               --)
-                       shift
-                       break
-                       ;;
-               -*)
-                       # pass all options through to push_stash
-                       push_options="$push_options $1"
-                       ;;
-               *)
-                       break
-                       ;;
-               esac
-               shift
-       done
-
-       stash_msg="$*"
-
-       if test -z "$stash_msg"
-       then
-               push_stash $push_options
-       else
-               push_stash $push_options -m "$stash_msg"
-       fi
-}
-
-have_stash () {
-       git rev-parse --verify --quiet $ref_stash >/dev/null
-}
-
-list_stash () {
-       have_stash || return 0
-       git log --format="%gd: %gs" -g --first-parent -m "$@" $ref_stash --
-}
-
-show_stash () {
-       ALLOW_UNKNOWN_FLAGS=t
-       assert_stash_like "$@"
-
-       if test -z "$FLAGS"
-       then
-               if test "$(git config --bool stash.showStat || echo true)" = "true"
-               then
-                       FLAGS=--stat
-               fi
-
-               if test "$(git config --bool stash.showPatch || echo false)" = "true"
-               then
-                       FLAGS=${FLAGS}${FLAGS:+ }-p
-               fi
-
-               if test -z "$FLAGS"
-               then
-                       return 0
-               fi
-       fi
-
-       git diff ${FLAGS} $b_commit $w_commit
-}
-
-show_help () {
-       exec git help stash
-       exit 1
-}
-
-#
-# Parses the remaining options looking for flags and
-# at most one revision defaulting to ${ref_stash}@{0}
-# if none found.
-#
-# Derives related tree and commit objects from the
-# revision, if one is found.
-#
-# stash records the work tree, and is a merge between the
-# base commit (first parent) and the index tree (second parent).
-#
-#   REV is set to the symbolic version of the specified stash-like commit
-#   IS_STASH_LIKE is non-blank if ${REV} looks like a stash
-#   IS_STASH_REF is non-blank if the ${REV} looks like a stash ref
-#   s is set to the SHA1 of the stash commit
-#   w_commit is set to the commit containing the working tree
-#   b_commit is set to the base commit
-#   i_commit is set to the commit containing the index tree
-#   u_commit is set to the commit containing the untracked files tree
-#   w_tree is set to the working tree
-#   b_tree is set to the base tree
-#   i_tree is set to the index tree
-#   u_tree is set to the untracked files tree
-#
-#   GIT_QUIET is set to t if -q is specified
-#   INDEX_OPTION is set to --index if --index is specified.
-#   FLAGS is set to the remaining flags (if allowed)
-#
-# dies if:
-#   * too many revisions specified
-#   * no revision is specified and there is no stash stack
-#   * a revision is specified which cannot be resolve to a SHA1
-#   * a non-existent stash reference is specified
-#   * unknown flags were set and ALLOW_UNKNOWN_FLAGS is not "t"
-#
-
-parse_flags_and_rev()
-{
-       test "$PARSE_CACHE" = "$*" && return 0 # optimisation
-       PARSE_CACHE="$*"
-
-       IS_STASH_LIKE=
-       IS_STASH_REF=
-       INDEX_OPTION=
-       s=
-       w_commit=
-       b_commit=
-       i_commit=
-       u_commit=
-       w_tree=
-       b_tree=
-       i_tree=
-       u_tree=
-
-       FLAGS=
-       REV=
-       for opt
-       do
-               case "$opt" in
-                       -q|--quiet)
-                               GIT_QUIET=-t
-                       ;;
-                       --index)
-                               INDEX_OPTION=--index
-                       ;;
-                       --help)
-                               show_help
-                       ;;
-                       -*)
-                               test "$ALLOW_UNKNOWN_FLAGS" = t ||
-                                       die "$(eval_gettext "unknown option: \$opt")"
-                               FLAGS="${FLAGS}${FLAGS:+ }$opt"
-                       ;;
-                       *)
-                               REV="${REV}${REV:+ }'$opt'"
-                       ;;
-               esac
-       done
-
-       eval set -- $REV
-
-       case $# in
-               0)
-                       have_stash || die "$(gettext "No stash entries found.")"
-                       set -- ${ref_stash}@{0}
-               ;;
-               1)
-                       :
-               ;;
-               *)
-                       die "$(eval_gettext "Too many revisions specified: \$REV")"
-               ;;
-       esac
-
-       case "$1" in
-               *[!0-9]*)
-                       :
-               ;;
-               *)
-                       set -- "${ref_stash}@{$1}"
-               ;;
-       esac
-
-       REV=$(git rev-parse --symbolic --verify --quiet "$1") || {
-               reference="$1"
-               die "$(eval_gettext "\$reference is not a valid reference")"
-       }
-
-       i_commit=$(git rev-parse --verify --quiet "$REV^2") &&
-       set -- $(git rev-parse "$REV" "$REV^1" "$REV:" "$REV^1:" "$REV^2:" 2>/dev/null) &&
-       s=$1 &&
-       w_commit=$1 &&
-       b_commit=$2 &&
-       w_tree=$3 &&
-       b_tree=$4 &&
-       i_tree=$5 &&
-       IS_STASH_LIKE=t &&
-       test "$ref_stash" = "$(git rev-parse --symbolic-full-name "${REV%@*}")" &&
-       IS_STASH_REF=t
-
-       u_commit=$(git rev-parse --verify --quiet "$REV^3") &&
-       u_tree=$(git rev-parse "$REV^3:" 2>/dev/null)
-}
-
-is_stash_like()
-{
-       parse_flags_and_rev "$@"
-       test -n "$IS_STASH_LIKE"
-}
-
-assert_stash_like() {
-       is_stash_like "$@" || {
-               args="$*"
-               die "$(eval_gettext "'\$args' is not a stash-like commit")"
-       }
-}
-
-is_stash_ref() {
-       is_stash_like "$@" && test -n "$IS_STASH_REF"
-}
-
-assert_stash_ref() {
-       is_stash_ref "$@" || {
-               args="$*"
-               die "$(eval_gettext "'\$args' is not a stash reference")"
-       }
-}
-
-apply_stash () {
-
-       assert_stash_like "$@"
-
-       git update-index -q --refresh || die "$(gettext "unable to refresh index")"
-
-       # current index state
-       c_tree=$(git write-tree) ||
-               die "$(gettext "Cannot apply a stash in the middle of a merge")"
-
-       unstashed_index_tree=
-       if test -n "$INDEX_OPTION" && test "$b_tree" != "$i_tree" &&
-                       test "$c_tree" != "$i_tree"
-       then
-               git diff-tree --binary $s^2^..$s^2 | git apply --cached
-               test $? -ne 0 &&
-                       die "$(gettext "Conflicts in index. Try without --index.")"
-               unstashed_index_tree=$(git write-tree) ||
-                       die "$(gettext "Could not save index tree")"
-               git reset
-       fi
-
-       if test -n "$u_tree"
-       then
-               GIT_INDEX_FILE="$TMPindex" git read-tree "$u_tree" &&
-               GIT_INDEX_FILE="$TMPindex" git checkout-index --all &&
-               rm -f "$TMPindex" ||
-               die "$(gettext "Could not restore untracked files from stash entry")"
-       fi
-
-       eval "
-               GITHEAD_$w_tree='Stashed changes' &&
-               GITHEAD_$c_tree='Updated upstream' &&
-               GITHEAD_$b_tree='Version stash was based on' &&
-               export GITHEAD_$w_tree GITHEAD_$c_tree GITHEAD_$b_tree
-       "
-
-       if test -n "$GIT_QUIET"
-       then
-               GIT_MERGE_VERBOSITY=0 && export GIT_MERGE_VERBOSITY
-       fi
-       if git merge-recursive $b_tree -- $c_tree $w_tree
-       then
-               # No conflict
-               if test -n "$unstashed_index_tree"
-               then
-                       git read-tree "$unstashed_index_tree"
-               else
-                       a="$TMP-added" &&
-                       git diff-index --cached --name-only --diff-filter=A $c_tree >"$a" &&
-                       git read-tree --reset $c_tree &&
-                       git update-index --add --stdin <"$a" ||
-                               die "$(gettext "Cannot unstage modified files")"
-                       rm -f "$a"
-               fi
-               squelch=
-               if test -n "$GIT_QUIET"
-               then
-                       squelch='>/dev/null 2>&1'
-               fi
-               (cd "$START_DIR" && eval "git status $squelch") || :
-       else
-               # Merge conflict; keep the exit status from merge-recursive
-               status=$?
-               git rerere
-               if test -n "$INDEX_OPTION"
-               then
-                       gettextln "Index was not unstashed." >&2
-               fi
-               exit $status
-       fi
-}
-
-pop_stash() {
-       assert_stash_ref "$@"
-
-       if apply_stash "$@"
-       then
-               drop_stash "$@"
-       else
-               status=$?
-               say "$(gettext "The stash entry is kept in case you need it again.")"
-               exit $status
-       fi
-}
-
-drop_stash () {
-       assert_stash_ref "$@"
-
-       git reflog delete --updateref --rewrite "${REV}" &&
-               say "$(eval_gettext "Dropped \${REV} (\$s)")" ||
-               die "$(eval_gettext "\${REV}: Could not drop stash entry")"
-
-       # clear_stash if we just dropped the last stash entry
-       git rev-parse --verify --quiet "$ref_stash@{0}" >/dev/null ||
-       clear_stash
-}
-
-apply_to_branch () {
-       test -n "$1" || die "$(gettext "No branch name specified")"
-       branch=$1
-       shift 1
-
-       set -- --index "$@"
-       assert_stash_like "$@"
-
-       git checkout -b $branch $REV^ &&
-       apply_stash "$@" && {
-               test -z "$IS_STASH_REF" || drop_stash "$@"
-       }
-}
-
-test "$1" = "-p" && set "push" "$@"
-
-PARSE_CACHE='--not-parsed'
-# The default command is "push" if nothing but options are given
-seen_non_option=
-for opt
-do
-       case "$opt" in
-       --) break ;;
-       -*) ;;
-       *) seen_non_option=t; break ;;
-       esac
-done
-
-test -n "$seen_non_option" || set "push" "$@"
-
-# Main command set
-case "$1" in
-list)
-       shift
-       list_stash "$@"
-       ;;
-show)
-       shift
-       show_stash "$@"
-       ;;
-save)
-       shift
-       save_stash "$@"
-       ;;
-push)
-       shift
-       push_stash "$@"
-       ;;
-apply)
-       shift
-       apply_stash "$@"
-       ;;
-clear)
-       shift
-       clear_stash "$@"
-       ;;
-create)
-       shift
-       create_stash -m "$*" && echo "$w_commit"
-       ;;
-store)
-       shift
-       store_stash "$@"
-       ;;
-drop)
-       shift
-       drop_stash "$@"
-       ;;
-pop)
-       shift
-       pop_stash "$@"
-       ;;
-branch)
-       shift
-       apply_to_branch "$@"
-       ;;
-*)
-       case $# in
-       0)
-               push_stash &&
-               say "$(gettext "(To restore them type \"git stash apply\")")"
-               ;;
-       *)
-               usage
-       esac
-       ;;
-esac
index 514ede259660d387fd392b950facc706f7c4d5e7..2c0fb6d723b74bb5bccba1ea49f97cdf23def361 100755 (executable)
@@ -594,7 +594,7 @@ cmd_update()
                                # is not reachable from a ref.
                                is_tip_reachable "$sm_path" "$sha1" ||
                                fetch_in_submodule "$sm_path" $depth ||
-                               say "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'")"
+                               say "$(eval_gettext "Unable to fetch in submodule path '\$displaypath'; trying to directly fetch \$sha1:")"
 
                                # Now we tried the usual fetch, but $sha1 may
                                # not be reachable from any of the refs
diff --git a/git.c b/git.c
index 2014aab6b83c61695d50ac39a18864a8d77858e0..1c4fa3e3efdb6a086cb696f01bf5844a7d6aae91 100644 (file)
--- a/git.c
+++ b/git.c
@@ -62,6 +62,13 @@ static int list_cmds(const char *spec)
 {
        struct string_list list = STRING_LIST_INIT_DUP;
        int i;
+       int nongit;
+
+       /*
+       * Set up the repository so we can pick up any repo-level config (like
+       * completion.commands).
+       */
+       setup_git_directory_gently(&nongit);
 
        while (*spec) {
                const char *sep = strchrnul(spec, ',');
@@ -570,6 +577,12 @@ static struct cmd_struct commands[] = {
        { "show-index", cmd_show_index },
        { "show-ref", cmd_show_ref, RUN_SETUP },
        { "stage", cmd_add, RUN_SETUP | NEED_WORK_TREE },
+       /*
+        * NEEDSWORK: Until the builtin stash is thoroughly robust and no
+        * longer needs redirection to the stash shell script this is kept as
+        * is, then should be changed to RUN_SETUP | NEED_WORK_TREE
+        */
+       { "stash", cmd_stash },
        { "status", cmd_status, RUN_SETUP | NEED_WORK_TREE },
        { "stripspace", cmd_stripspace },
        { "submodule--helper", cmd_submodule__helper, RUN_SETUP | SUPPORT_SUPER_PREFIX | NO_PARSEOPT },
diff --git a/help.c b/help.c
index 520c9080e8e4abf7793af0966588aa370f09d316..a9e451f2ee7a165eecb17746db9cc1a2f7e2cfbb 100644 (file)
--- a/help.c
+++ b/help.c
@@ -375,13 +375,6 @@ void list_cmds_by_config(struct string_list *list)
 {
        const char *cmd_list;
 
-       /*
-        * There's no actual repository setup at this point (and even
-        * if there is, we don't really care; only global config
-        * matters). If we accidentally set up a repository, it's ok
-        * too since the caller (git --list-cmds=) should exit shortly
-        * anyway.
-        */
        if (git_config_get_string_const("completion.commands", &cmd_list))
                return;
 
@@ -393,8 +386,8 @@ void list_cmds_by_config(struct string_list *list)
                const char *p = strchrnul(cmd_list, ' ');
 
                strbuf_add(&sb, cmd_list, p - cmd_list);
-               if (*cmd_list == '-')
-                       string_list_remove(list, cmd_list + 1, 0);
+               if (sb.buf[0] == '-')
+                       string_list_remove(list, sb.buf + 1, 0);
                else
                        string_list_insert(list, sb.buf);
                strbuf_release(&sb);
index 8ae5d76c6aa216e92c26b875f72b7bb991b6e3cd..48b1b3a193e9e08ec12ff725661302ddd3d2b9bf 100644 (file)
@@ -98,6 +98,11 @@ static void process_object_response(void *callback_data)
        process_http_object_request(obj_req->req);
        obj_req->state = COMPLETE;
 
+       normalize_curl_result(&obj_req->req->curl_result,
+                             obj_req->req->http_code,
+                             obj_req->req->errorstr,
+                             sizeof(obj_req->req->errorstr));
+
        /* Use alternates if necessary */
        if (missing_target(obj_req->req)) {
                fetch_alternates(walker, alt->base);
@@ -208,6 +213,9 @@ static void process_alternates_response(void *callback_data)
        char *data;
        int i = 0;
 
+       normalize_curl_result(&slot->curl_result, slot->http_code,
+                             curl_errorstr, sizeof(curl_errorstr));
+
        if (alt_req->http_specific) {
                if (slot->curl_result != CURLE_OK ||
                    !alt_req->buffer->len) {
@@ -518,17 +526,8 @@ static int fetch_object(struct walker *walker, unsigned char *sha1)
                req->localfile = -1;
        }
 
-       /*
-        * we turned off CURLOPT_FAILONERROR to avoid losing a
-        * persistent connection and got CURLE_OK.
-        */
-       if (req->http_code >= 300 && req->curl_result == CURLE_OK &&
-                       (starts_with(req->url, "http://") ||
-                        starts_with(req->url, "https://"))) {
-               req->curl_result = CURLE_HTTP_RETURNED_ERROR;
-               xsnprintf(req->errorstr, sizeof(req->errorstr),
-                         "HTTP request failed");
-       }
+       normalize_curl_result(&req->curl_result, req->http_code,
+                             req->errorstr, sizeof(req->errorstr));
 
        if (obj_req->state == ABORTED) {
                ret = error("Request for %s aborted", hex);
diff --git a/http.c b/http.c
index a32ad36ddf6b813ce0e02c85e7b61b4613517a9c..89fcd36a80821464dc15c10cfe9c8a424256aeba 100644 (file)
--- a/http.c
+++ b/http.c
@@ -1544,7 +1544,8 @@ char *get_remote_object_url(const char *url, const char *hex,
        return strbuf_detach(&buf, NULL);
 }
 
-static int handle_curl_result(struct slot_results *results)
+void normalize_curl_result(CURLcode *result, long http_code,
+                          char *errorstr, size_t errorlen)
 {
        /*
         * If we see a failing http code with CURLE_OK, we have turned off
@@ -1554,19 +1555,24 @@ static int handle_curl_result(struct slot_results *results)
         * Likewise, if we see a redirect (30x code), that means we turned off
         * redirect-following, and we should treat the result as an error.
         */
-       if (results->curl_result == CURLE_OK &&
-           results->http_code >= 300) {
-               results->curl_result = CURLE_HTTP_RETURNED_ERROR;
+       if (*result == CURLE_OK && http_code >= 300) {
+               *result = CURLE_HTTP_RETURNED_ERROR;
                /*
                 * Normally curl will already have put the "reason phrase"
                 * from the server into curl_errorstr; unfortunately without
                 * FAILONERROR it is lost, so we can give only the numeric
                 * status code.
                 */
-               xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+               xsnprintf(errorstr, errorlen,
                          "The requested URL returned error: %ld",
-                         results->http_code);
+                         http_code);
        }
+}
+
+static int handle_curl_result(struct slot_results *results)
+{
+       normalize_curl_result(&results->curl_result, results->http_code,
+                             curl_errorstr, sizeof(curl_errorstr));
 
        if (results->curl_result == CURLE_OK) {
                credential_approve(&http_auth);
diff --git a/http.h b/http.h
index 4eb4e808e5731a3a9a650ca4c1efb3e471604d05..f0d271bb7b9eadcec3890ae3e850f9ef8f91186c 100644 (file)
--- a/http.h
+++ b/http.h
@@ -136,6 +136,15 @@ static inline int missing__target(int code, int result)
 
 #define missing_target(a) missing__target((a)->http_code, (a)->curl_result)
 
+/*
+ * Normalize curl results to handle CURL_FAILONERROR (or lack thereof). Failing
+ * http codes have their "result" converted to CURLE_HTTP_RETURNED_ERROR, and
+ * an appropriate string placed in the errorstr buffer (pass curl_errorstr if
+ * you don't have a custom buffer).
+ */
+void normalize_curl_result(CURLcode *result, long http_code, char *errorstr,
+                          size_t errorlen);
+
 /* Helpers for modifying and creating URLs */
 extern void append_remote_object_url(struct strbuf *buf, const char *url,
                                     const char *hex,
diff --git a/ident.c b/ident.c
index 9c2eb0a2d02ac823a6bf3f01603dfdc7164728c8..e666ee4e598eb7de70b524bd56c7d04a27549971 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -585,6 +585,26 @@ int git_ident_config(const char *var, const char *value, void *data)
        return set_ident(var, value);
 }
 
+static void set_env_if(const char *key, const char *value, int *given, int bit)
+{
+       if ((*given & bit) || getenv(key))
+               return; /* nothing to do */
+       setenv(key, value, 0);
+       *given |= bit;
+}
+
+void prepare_fallback_ident(const char *name, const char *email)
+{
+       set_env_if("GIT_AUTHOR_NAME", name,
+                  &author_ident_explicitly_given, IDENT_NAME_GIVEN);
+       set_env_if("GIT_AUTHOR_EMAIL", email,
+                  &author_ident_explicitly_given, IDENT_MAIL_GIVEN);
+       set_env_if("GIT_COMMITTER_NAME", name,
+                  &committer_ident_explicitly_given, IDENT_NAME_GIVEN);
+       set_env_if("GIT_COMMITTER_EMAIL", email,
+                  &committer_ident_explicitly_given, IDENT_MAIL_GIVEN);
+}
+
 static int buf_cmp(const char *a_begin, const char *a_end,
                   const char *b_begin, const char *b_end)
 {
index 24e21731c4315f414578b33178a7b784176bb24d..59248e37cc3cec8fd309df77fb58b09c8612f336 100644 (file)
@@ -1103,10 +1103,12 @@ static int process_all_files(struct line_log_data **range_out,
 
 int line_log_print(struct rev_info *rev, struct commit *commit)
 {
-       struct line_log_data *range = lookup_line_range(rev, commit);
 
        show_log(rev);
-       dump_diff_hacky(rev, range);
+       if (!(rev->diffopt.output_format & DIFF_FORMAT_NO_OUTPUT)) {
+               struct line_log_data *range = lookup_line_range(rev, commit);
+               dump_diff_hacky(rev, range);
+       }
        return 1;
 }
 
diff --git a/mergetools/smerge b/mergetools/smerge
new file mode 100644 (file)
index 0000000..9c2e6f6
--- /dev/null
@@ -0,0 +1,12 @@
+diff_cmd () {
+       "$merge_tool_path" mergetool "$LOCAL" "$REMOTE" -o "$MERGED"
+}
+
+merge_cmd () {
+       if $base_present
+       then
+               "$merge_tool_path" mergetool "$BASE" "$LOCAL" "$REMOTE" -o "$MERGED"
+       else
+               "$merge_tool_path" mergetool "$LOCAL" "$REMOTE" -o "$MERGED"
+       fi
+}
diff --git a/midx.c b/midx.c
index 8a505fd423efccf9986079542b47967cc93df7dc..f1137eb6ee7bb2d16062399965671cfda0e95e52 100644 (file)
--- a/midx.c
+++ b/midx.c
@@ -8,6 +8,7 @@
 #include "sha1-lookup.h"
 #include "midx.h"
 #include "progress.h"
+#include "trace2.h"
 
 #define MIDX_SIGNATURE 0x4d494458 /* "MIDX" */
 #define MIDX_VERSION 1
@@ -70,7 +71,7 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
 
        midx_map = xmmap(NULL, midx_size, PROT_READ, MAP_PRIVATE, fd, 0);
 
-       FLEX_ALLOC_MEM(m, object_dir, object_dir, strlen(object_dir));
+       FLEX_ALLOC_STR(m, object_dir, object_dir);
        m->fd = fd;
        m->data = midx_map;
        m->data_len = midx_size;
@@ -164,6 +165,9 @@ struct multi_pack_index *load_multi_pack_index(const char *object_dir, int local
                              m->pack_names[i]);
        }
 
+       trace2_data_intmax("midx", the_repository, "load/num_packs", m->num_packs);
+       trace2_data_intmax("midx", the_repository, "load/num_objects", m->num_objects);
+
        return m;
 
 cleanup_fail:
@@ -958,8 +962,35 @@ static void midx_report(const char *fmt, ...)
        va_end(ap);
 }
 
+struct pair_pos_vs_id
+{
+       uint32_t pos;
+       uint32_t pack_int_id;
+};
+
+static int compare_pair_pos_vs_id(const void *_a, const void *_b)
+{
+       struct pair_pos_vs_id *a = (struct pair_pos_vs_id *)_a;
+       struct pair_pos_vs_id *b = (struct pair_pos_vs_id *)_b;
+
+       return b->pack_int_id - a->pack_int_id;
+}
+
+/*
+ * Limit calls to display_progress() for performance reasons.
+ * The interval here was arbitrarily chosen.
+ */
+#define SPARSE_PROGRESS_INTERVAL (1 << 12)
+#define midx_display_sparse_progress(progress, n) \
+       do { \
+               uint64_t _n = (n); \
+               if ((_n & (SPARSE_PROGRESS_INTERVAL - 1)) == 0) \
+                       display_progress(progress, _n); \
+       } while (0)
+
 int verify_midx_file(const char *object_dir)
 {
+       struct pair_pos_vs_id *pairs = NULL;
        uint32_t i;
        struct progress *progress;
        struct multi_pack_index *m = load_multi_pack_index(object_dir, 1);
@@ -968,10 +999,15 @@ int verify_midx_file(const char *object_dir)
        if (!m)
                return 0;
 
+       progress = start_progress(_("Looking for referenced packfiles"),
+                                 m->num_packs);
        for (i = 0; i < m->num_packs; i++) {
                if (prepare_midx_pack(m, i))
                        midx_report("failed to load pack in position %d", i);
+
+               display_progress(progress, i + 1);
        }
+       stop_progress(&progress);
 
        for (i = 0; i < 255; i++) {
                uint32_t oid_fanout1 = ntohl(m->chunk_oid_fanout[i]);
@@ -982,6 +1018,8 @@ int verify_midx_file(const char *object_dir)
                                    i, oid_fanout1, oid_fanout2, i + 1);
        }
 
+       progress = start_sparse_progress(_("Verifying OID order in MIDX"),
+                                        m->num_objects - 1);
        for (i = 0; i < m->num_objects - 1; i++) {
                struct object_id oid1, oid2;
 
@@ -991,18 +1029,47 @@ int verify_midx_file(const char *object_dir)
                if (oidcmp(&oid1, &oid2) >= 0)
                        midx_report(_("oid lookup out of order: oid[%d] = %s >= %s = oid[%d]"),
                                    i, oid_to_hex(&oid1), oid_to_hex(&oid2), i + 1);
+
+               midx_display_sparse_progress(progress, i + 1);
        }
+       stop_progress(&progress);
 
-       progress = start_progress(_("Verifying object offsets"), m->num_objects);
+       /*
+        * Create an array mapping each object to its packfile id.  Sort it
+        * to group the objects by packfile.  Use this permutation to visit
+        * each of the objects and only require 1 packfile to be open at a
+        * time.
+        */
+       ALLOC_ARRAY(pairs, m->num_objects);
+       for (i = 0; i < m->num_objects; i++) {
+               pairs[i].pos = i;
+               pairs[i].pack_int_id = nth_midxed_pack_int_id(m, i);
+       }
+
+       progress = start_sparse_progress(_("Sorting objects by packfile"),
+                                        m->num_objects);
+       display_progress(progress, 0); /* TODO: Measure QSORT() progress */
+       QSORT(pairs, m->num_objects, compare_pair_pos_vs_id);
+       stop_progress(&progress);
+
+       progress = start_sparse_progress(_("Verifying object offsets"), m->num_objects);
        for (i = 0; i < m->num_objects; i++) {
                struct object_id oid;
                struct pack_entry e;
                off_t m_offset, p_offset;
 
-               nth_midxed_object_oid(&oid, m, i);
+               if (i > 0 && pairs[i-1].pack_int_id != pairs[i].pack_int_id &&
+                   m->packs[pairs[i-1].pack_int_id])
+               {
+                       close_pack_fd(m->packs[pairs[i-1].pack_int_id]);
+                       close_pack_index(m->packs[pairs[i-1].pack_int_id]);
+               }
+
+               nth_midxed_object_oid(&oid, m, pairs[i].pos);
+
                if (!fill_midx_entry(&oid, &e, m)) {
                        midx_report(_("failed to load pack entry for oid[%d] = %s"),
-                                   i, oid_to_hex(&oid));
+                                   pairs[i].pos, oid_to_hex(&oid));
                        continue;
                }
 
@@ -1017,11 +1084,13 @@ int verify_midx_file(const char *object_dir)
 
                if (m_offset != p_offset)
                        midx_report(_("incorrect object offset for oid[%d] = %s: %"PRIx64" != %"PRIx64),
-                                   i, oid_to_hex(&oid), m_offset, p_offset);
+                                   pairs[i].pos, oid_to_hex(&oid), m_offset, p_offset);
 
-               display_progress(progress, i + 1);
+               midx_display_sparse_progress(progress, i + 1);
        }
        stop_progress(&progress);
 
+       free(pairs);
+
        return verify_midx_error;
 }
index 16bcb75262d918fc7b88cc5141213e951be145cc..d2bcb2f860c50056b7014b8a7dbf804baad79be8 100644 (file)
@@ -309,7 +309,7 @@ void close_pack_windows(struct packed_git *p)
        }
 }
 
-static int close_pack_fd(struct packed_git *p)
+int close_pack_fd(struct packed_git *p)
 {
        if (p->pack_fd < 0)
                return 0;
index d70c6d9afb94c77c285fe8ee3237f7a40867157a..b1c18504eb92cef76b7efca6790ba565a8f5e950 100644 (file)
@@ -76,6 +76,8 @@ extern int open_pack_index(struct packed_git *);
  */
 extern void close_pack_index(struct packed_git *);
 
+int close_pack_fd(struct packed_git *p);
+
 extern uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
 
 extern unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
index ade83a7b2006f50b058715f0340f696e17577607..bf2037f6e1cc307ba04d817620b534697c3609c9 100644 (file)
@@ -6,6 +6,8 @@
 #include "color.h"
 #include "utf8.h"
 
+static int disallow_abbreviated_options;
+
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
@@ -344,6 +346,10 @@ static enum parse_opt_result parse_long_opt(
                return get_value(p, options, all_opts, flags ^ opt_flags);
        }
 
+       if (disallow_abbreviated_options && (ambiguous_option || abbrev_option))
+               die("disallowed abbreviated or ambiguous option '%.*s'",
+                   (int)(arg_end - arg), arg);
+
        if (ambiguous_option) {
                error(_("ambiguous option: %s "
                        "(could be --%s%s or --%s%s)"),
@@ -707,6 +713,9 @@ int parse_options(int argc, const char **argv, const char *prefix,
 {
        struct parse_opt_ctx_t ctx;
 
+       disallow_abbreviated_options =
+               git_env_bool("GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS", 0);
+
        parse_options_start(&ctx, argc, argv, prefix, options, flags);
        switch (parse_options_step(&ctx, options, usagestr)) {
        case PARSE_OPT_HELP:
diff --git a/path.c b/path.c
index 03ab712839a7878e3d3b5d79c698f4e2bb29c8b1..25e97b8c3f76ce9246d8d985adba9777acd5f43c 100644 (file)
--- a/path.c
+++ b/path.c
@@ -115,10 +115,13 @@ static struct common_dir common_list[] = {
        { 1, 1, 0, "logs" },
        { 1, 1, 1, "logs/HEAD" },
        { 0, 1, 1, "logs/refs/bisect" },
+       { 0, 1, 1, "logs/refs/rewritten" },
+       { 0, 1, 1, "logs/refs/worktree" },
        { 0, 1, 0, "lost-found" },
        { 0, 1, 0, "objects" },
        { 0, 1, 0, "refs" },
        { 0, 1, 1, "refs/bisect" },
+       { 0, 1, 1, "refs/rewritten" },
        { 0, 1, 1, "refs/worktree" },
        { 0, 1, 0, "remotes" },
        { 0, 1, 0, "worktrees" },
index 5a99c9fbf00bfb98d3a51cc7a7948b4e963b3517..212d00e524819aafe17b55f1b2f54a5bcf40dd7e 100644 (file)
@@ -34,6 +34,7 @@ struct progress {
        uint64_t total;
        unsigned last_percent;
        unsigned delay;
+       unsigned sparse;
        struct throughput *throughput;
        uint64_t start_ns;
 };
@@ -194,7 +195,7 @@ int display_progress(struct progress *progress, uint64_t n)
 }
 
 static struct progress *start_progress_delay(const char *title, uint64_t total,
-                                            unsigned delay)
+                                            unsigned delay, unsigned sparse)
 {
        struct progress *progress = malloc(sizeof(*progress));
        if (!progress) {
@@ -208,6 +209,7 @@ static struct progress *start_progress_delay(const char *title, uint64_t total,
        progress->last_value = -1;
        progress->last_percent = -1;
        progress->delay = delay;
+       progress->sparse = sparse;
        progress->throughput = NULL;
        progress->start_ns = getnanotime();
        set_progress_signal();
@@ -216,16 +218,46 @@ static struct progress *start_progress_delay(const char *title, uint64_t total,
 
 struct progress *start_delayed_progress(const char *title, uint64_t total)
 {
-       return start_progress_delay(title, total, 2);
+       return start_progress_delay(title, total, 2, 0);
 }
 
 struct progress *start_progress(const char *title, uint64_t total)
 {
-       return start_progress_delay(title, total, 0);
+       return start_progress_delay(title, total, 0, 0);
+}
+
+/*
+ * Here "sparse" means that the caller might use some sampling criteria to
+ * decide when to call display_progress() rather than calling it for every
+ * integer value in[0 .. total).  In particular, the caller might not call
+ * display_progress() for the last value in the range.
+ *
+ * When "sparse" is set, stop_progress() will automatically force the done
+ * message to show 100%.
+ */
+struct progress *start_sparse_progress(const char *title, uint64_t total)
+{
+       return start_progress_delay(title, total, 0, 1);
+}
+
+struct progress *start_delayed_sparse_progress(const char *title,
+                                              uint64_t total)
+{
+       return start_progress_delay(title, total, 2, 1);
+}
+
+static void finish_if_sparse(struct progress *progress)
+{
+       if (progress &&
+           progress->sparse &&
+           progress->last_value != progress->total)
+               display_progress(progress, progress->total);
 }
 
 void stop_progress(struct progress **p_progress)
 {
+       finish_if_sparse(*p_progress);
+
        stop_progress_msg(p_progress, _("done"));
 }
 
index 70a4d4a0d66ebda2423acf4b5dc84ba485a874b8..7b725acc8d7b6cbaa5e36743b42b29d8c22d868d 100644 (file)
@@ -6,7 +6,10 @@ struct progress;
 void display_throughput(struct progress *progress, uint64_t total);
 int display_progress(struct progress *progress, uint64_t n);
 struct progress *start_progress(const char *title, uint64_t total);
+struct progress *start_sparse_progress(const char *title, uint64_t total);
 struct progress *start_delayed_progress(const char *title, uint64_t total);
+struct progress *start_delayed_sparse_progress(const char *title,
+                                              uint64_t total);
 void stop_progress(struct progress **progress);
 void stop_progress_msg(struct progress **progress, const char *msg);
 
index 5e636785d14f8ef5283561d4606fface080bf3c0..9741f057505d2041be1e8dbd49b444aaeab4bd82 100644 (file)
@@ -17,6 +17,10 @@ static enum protocol_version parse_protocol_version(const char *value)
 enum protocol_version get_protocol_version_config(void)
 {
        const char *value;
+       enum protocol_version retval = protocol_v0;
+       const char *git_test_k = "GIT_TEST_PROTOCOL_VERSION";
+       const char *git_test_v = getenv(git_test_k);
+
        if (!git_config_get_string_const("protocol.version", &value)) {
                enum protocol_version version = parse_protocol_version(value);
 
@@ -24,10 +28,19 @@ enum protocol_version get_protocol_version_config(void)
                        die("unknown value for config 'protocol.version': %s",
                            value);
 
-               return version;
+               retval = version;
+       }
+
+       if (git_test_v && *git_test_v) {
+               enum protocol_version env = parse_protocol_version(git_test_v);
+
+               if (env == protocol_unknown_version)
+                       die("unknown value for %s: %s", git_test_k, git_test_v);
+               if (retval < env)
+                       retval = env;
        }
 
-       return protocol_v0;
+       return retval;
 }
 
 enum protocol_version determine_protocol_version_server(void)
index 4dc6de1b55b0a6047e49f188b28c431f0167d7ef..c62eae651df112b94a8a17ae80e399687a570572 100644 (file)
@@ -17,6 +17,7 @@
 #include "commit.h"
 #include "blob.h"
 #include "resolve-undo.h"
+#include "run-command.h"
 #include "strbuf.h"
 #include "varint.h"
 #include "split-index.h"
@@ -3049,8 +3050,17 @@ static int do_write_locked_index(struct index_state *istate, struct lock_file *l
        if (ret)
                return ret;
        if (flags & COMMIT_LOCK)
-               return commit_locked_index(lock);
-       return close_lock_file_gently(lock);
+               ret = commit_locked_index(lock);
+       else
+               ret = close_lock_file_gently(lock);
+
+       run_hook_le(NULL, "post-index-change",
+                       istate->updated_workdir ? "1" : "0",
+                       istate->updated_skipworktree ? "1" : "0", NULL);
+       istate->updated_workdir = 0;
+       istate->updated_skipworktree = 0;
+
+       return ret;
 }
 
 static int write_split_index(struct index_state *istate,
index 68aff1dac28878f2f9c010459bf5f4362322bd3f..aa18ae82b724812ea0b2939842395ac78f77bab1 100644 (file)
@@ -1,10 +1,35 @@
 #include "cache.h"
 #include "commit.h"
-#include "rebase-interactive.h"
 #include "sequencer.h"
+#include "rebase-interactive.h"
 #include "strbuf.h"
+#include "commit-slab.h"
+#include "config.h"
+
+enum missing_commit_check_level {
+       MISSING_COMMIT_CHECK_IGNORE = 0,
+       MISSING_COMMIT_CHECK_WARN,
+       MISSING_COMMIT_CHECK_ERROR
+};
+
+static enum missing_commit_check_level get_missing_commit_check_level(void)
+{
+       const char *value;
+
+       if (git_config_get_value("rebase.missingcommitscheck", &value) ||
+                       !strcasecmp("ignore", value))
+               return MISSING_COMMIT_CHECK_IGNORE;
+       if (!strcasecmp("warn", value))
+               return MISSING_COMMIT_CHECK_WARN;
+       if (!strcasecmp("error", value))
+               return MISSING_COMMIT_CHECK_ERROR;
+       warning(_("unrecognized setting %s for option "
+                 "rebase.missingCommitsCheck. Ignoring."), value);
+       return MISSING_COMMIT_CHECK_IGNORE;
+}
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+                     const char *shortrevisions, const char *shortonto,
                      struct strbuf *buf)
 {
        const char *msg = _("\nCommands:\n"
@@ -24,6 +49,15 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,
 ".       specified). Use -c <commit> to reword the commit message.\n"
 "\n"
 "These lines can be re-ordered; they are executed from top to bottom.\n");
+       unsigned edit_todo = !(shortrevisions && shortonto);
+
+       if (!edit_todo) {
+               strbuf_addch(buf, '\n');
+               strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
+                                             "Rebase %s onto %s (%d commands)",
+                                             command_count),
+                                     shortrevisions, shortonto, command_count);
+       }
 
        strbuf_add_commented_lines(buf, msg, strlen(msg));
 
@@ -53,39 +87,103 @@ void append_todo_help(unsigned edit_todo, unsigned keep_empty,
        }
 }
 
-int edit_todo_list(struct repository *r, unsigned flags)
+int edit_todo_list(struct repository *r, struct todo_list *todo_list,
+                  struct todo_list *new_todo, const char *shortrevisions,
+                  const char *shortonto, unsigned flags)
 {
-       struct strbuf buf = STRBUF_INIT;
        const char *todo_file = rebase_path_todo();
+       unsigned initial = shortrevisions && shortonto;
 
-       if (strbuf_read_file(&buf, todo_file, 0) < 0)
-               return error_errno(_("could not read '%s'."), todo_file);
+       /* If the user is editing the todo list, we first try to parse
+        * it.  If there is an error, we do not return, because the user
+        * might want to fix it in the first place. */
+       if (!initial)
+               todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
 
-       strbuf_stripspace(&buf, 1);
-       if (write_message(buf.buf, buf.len, todo_file, 0)) {
-               strbuf_release(&buf);
-               return -1;
-       }
+       if (todo_list_write_to_file(r, todo_list, todo_file, shortrevisions, shortonto,
+                                   -1, flags | TODO_LIST_SHORTEN_IDS | TODO_LIST_APPEND_TODO_HELP))
+               return error_errno(_("could not write '%s'"), todo_file);
+
+       if (initial && copy_file(rebase_path_todo_backup(), todo_file, 0666))
+               return error(_("could not copy '%s' to '%s'."), todo_file,
+                            rebase_path_todo_backup());
 
-       strbuf_release(&buf);
+       if (launch_sequence_editor(todo_file, &new_todo->buf, NULL))
+               return -2;
 
-       transform_todos(r, flags | TODO_LIST_SHORTEN_IDS);
+       strbuf_stripspace(&new_todo->buf, 1);
+       if (initial && new_todo->buf.len == 0)
+               return -3;
 
-       if (strbuf_read_file(&buf, todo_file, 0) < 0)
-               return error_errno(_("could not read '%s'."), todo_file);
+       /* For the initial edit, the todo list gets parsed in
+        * complete_action(). */
+       if (!initial)
+               return todo_list_parse_insn_buffer(r, new_todo->buf.buf, new_todo);
 
-       append_todo_help(1, 0, &buf);
-       if (write_message(buf.buf, buf.len, todo_file, 0)) {
-               strbuf_release(&buf);
-               return -1;
+       return 0;
+}
+
+define_commit_slab(commit_seen, unsigned char);
+/*
+ * Check if the user dropped some commits by mistake
+ * Behaviour determined by rebase.missingCommitsCheck.
+ * Check if there is an unrecognized command or a
+ * bad SHA-1 in a command.
+ */
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo)
+{
+       enum missing_commit_check_level check_level = get_missing_commit_check_level();
+       struct strbuf missing = STRBUF_INIT;
+       int res = 0, i;
+       struct commit_seen commit_seen;
+
+       init_commit_seen(&commit_seen);
+
+       if (check_level == MISSING_COMMIT_CHECK_IGNORE)
+               goto leave_check;
+
+       /* Mark the commits in git-rebase-todo as seen */
+       for (i = 0; i < new_todo->nr; i++) {
+               struct commit *commit = new_todo->items[i].commit;
+               if (commit)
+                       *commit_seen_at(&commit_seen, commit) = 1;
        }
 
-       strbuf_release(&buf);
+       /* Find commits in git-rebase-todo.backup yet unseen */
+       for (i = old_todo->nr - 1; i >= 0; i--) {
+               struct todo_item *item = old_todo->items + i;
+               struct commit *commit = item->commit;
+               if (commit && !*commit_seen_at(&commit_seen, commit)) {
+                       strbuf_addf(&missing, " - %s %.*s\n",
+                                   find_unique_abbrev(&commit->object.oid, DEFAULT_ABBREV),
+                                   item->arg_len,
+                                   todo_item_get_arg(old_todo, item));
+                       *commit_seen_at(&commit_seen, commit) = 1;
+               }
+       }
 
-       if (launch_sequence_editor(todo_file, NULL, NULL))
-               return -1;
+       /* Warn about missing commits */
+       if (!missing.len)
+               goto leave_check;
 
-       transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS));
+       if (check_level == MISSING_COMMIT_CHECK_ERROR)
+               res = 1;
 
-       return 0;
+       fprintf(stderr,
+               _("Warning: some commits may have been dropped accidentally.\n"
+               "Dropped commits (newer to older):\n"));
+
+       /* Make the list user-friendly and display */
+       fputs(missing.buf, stderr);
+       strbuf_release(&missing);
+
+       fprintf(stderr, _("To avoid this message, use \"drop\" to "
+               "explicitly remove a commit.\n\n"
+               "Use 'git config rebase.missingCommitsCheck' to change "
+               "the level of warnings.\n"
+               "The possible behaviours are: ignore, warn, error.\n\n"));
+
+leave_check:
+       clear_commit_seen(&commit_seen);
+       return res;
 }
index 17b6c9f6d05ea2372c58a50679cb0f8fb808aaef..44dbb06311a84168d8a5970267a14ade85671a98 100644 (file)
@@ -3,9 +3,14 @@
 
 struct strbuf;
 struct repository;
+struct todo_list;
 
-void append_todo_help(unsigned edit_todo, unsigned keep_empty,
+void append_todo_help(unsigned keep_empty, int command_count,
+                     const char *shortrevisions, const char *shortonto,
                      struct strbuf *buf);
-int edit_todo_list(struct repository *r, unsigned flags);
+int edit_todo_list(struct repository *r, struct todo_list *todo_list,
+                  struct todo_list *new_todo, const char *shortrevisions,
+                  const char *shortonto, unsigned flags);
+int todo_list_check(struct todo_list *old_todo, struct todo_list *new_todo);
 
 #endif
index ef053f716c300ec13a06a63119bc7e3a289d2494..63e55e67730fcf01d8ca545e15b3c1e5b50c4b06 100644 (file)
@@ -214,6 +214,33 @@ static void files_ref_path(struct files_ref_store *refs,
        }
 }
 
+/*
+ * Manually add refs/bisect, refs/rewritten and refs/worktree, which, being
+ * per-worktree, might not appear in the directory listing for
+ * refs/ in the main repo.
+ */
+static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dirname)
+{
+       const char *prefixes[] = { "refs/bisect/", "refs/worktree/", "refs/rewritten/" };
+       int ip;
+
+       if (strcmp(dirname, "refs/"))
+               return;
+
+       for (ip = 0; ip < ARRAY_SIZE(prefixes); ip++) {
+               const char *prefix = prefixes[ip];
+               int prefix_len = strlen(prefix);
+               struct ref_entry *child_entry;
+               int pos;
+
+               pos = search_ref_dir(dir, prefix, prefix_len);
+               if (pos >= 0)
+                       continue;
+               child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1);
+               add_entry_to_dir(dir, child_entry);
+       }
+}
+
 /*
  * Read the loose references from the namespace dirname into dir
  * (without recursing).  dirname must end with '/'.  dir must be the
@@ -297,28 +324,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        strbuf_release(&path);
        closedir(d);
 
-       /*
-        * Manually add refs/bisect and refs/worktree, which, being
-        * per-worktree, might not appear in the directory listing for
-        * refs/ in the main repo.
-        */
-       if (!strcmp(dirname, "refs/")) {
-               int pos = search_ref_dir(dir, "refs/bisect/", 12);
-
-               if (pos < 0) {
-                       struct ref_entry *child_entry = create_dir_entry(
-                                       dir->cache, "refs/bisect/", 12, 1);
-                       add_entry_to_dir(dir, child_entry);
-               }
-
-               pos = search_ref_dir(dir, "refs/worktree/", 11);
-
-               if (pos < 0) {
-                       struct ref_entry *child_entry = create_dir_entry(
-                                       dir->cache, "refs/worktree/", 11, 1);
-                       add_entry_to_dir(dir, child_entry);
-               }
-       }
+       add_per_worktree_entries_to_dir(dir, dirname);
 }
 
 static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
@@ -2695,18 +2701,32 @@ static int files_transaction_prepare(struct ref_store *ref_store,
                if (is_packed_transaction_needed(refs->packed_ref_store,
                                                 packed_transaction)) {
                        ret = ref_transaction_prepare(packed_transaction, err);
+                       /*
+                        * A failure during the prepare step will abort
+                        * itself, but not free. Do that now, and disconnect
+                        * from the files_transaction so it does not try to
+                        * abort us when we hit the cleanup code below.
+                        */
+                       if (ret) {
+                               ref_transaction_free(packed_transaction);
+                               backend_data->packed_transaction = NULL;
+                       }
                } else {
                        /*
                         * We can skip rewriting the `packed-refs`
                         * file. But we do need to leave it locked, so
                         * that somebody else doesn't pack a reference
                         * that we are trying to delete.
+                        *
+                        * We need to disconnect our transaction from
+                        * backend_data, since the abort (whether successful or
+                        * not) will free it.
                         */
+                       backend_data->packed_transaction = NULL;
                        if (ref_transaction_abort(packed_transaction, err)) {
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
-                       backend_data->packed_transaction = NULL;
                }
        }
 
index 5b447949228cbf853f35ff9c3cccd4ea7a0f8afc..8bba57270b00b72c7e70fe749880a29c1ddfea1f 100644 (file)
@@ -16,6 +16,7 @@
 #include "send-pack.h"
 #include "protocol.h"
 #include "quote.h"
+#include "transport.h"
 
 static struct remote *remote;
 /* always ends with a trailing slash */
@@ -153,7 +154,7 @@ static int set_option(const char *name, const char *value)
                else {
                        struct strbuf unquoted = STRBUF_INIT;
                        if (unquote_c_style(&unquoted, value, NULL) < 0)
-                               die("invalid quoting in push-option value");
+                               die(_("invalid quoting in push-option value: '%s'"), value);
                        string_list_append_nodup(&options.push_options,
                                                 strbuf_detach(&unquoted, NULL));
                }
@@ -250,8 +251,8 @@ static struct ref *parse_info_refs(struct discovery *heads)
                        mid = &data[i];
                if (data[i] == '\n') {
                        if (mid - start != 40)
-                               die("%sinfo/refs not valid: is this a git repository?",
-                                   url.buf);
+                               die(_("%sinfo/refs not valid: is this a git repository?"),
+                                   transport_anonymize_url(url.buf));
                        data[i] = 0;
                        ref_name = mid + 1;
                        ref = alloc_ref(ref_name);
@@ -351,7 +352,7 @@ static void check_smart_http(struct discovery *d, const char *service,
                           PACKET_READ_CHOMP_NEWLINE |
                           PACKET_READ_DIE_ON_ERR_PACKET);
        if (packet_reader_read(&reader) != PACKET_READ_NORMAL)
-               die("invalid server response; expected service, got flush packet");
+               die(_("invalid server response; expected service, got flush packet"));
 
        if (skip_prefix(reader.line, "# service=", &p) && !strcmp(p, service)) {
                /*
@@ -382,7 +383,7 @@ static void check_smart_http(struct discovery *d, const char *service,
                d->proto_git = 1;
 
        } else {
-               die("invalid server response; got '%s'", reader.line);
+               die(_("invalid server response; got '%s'"), reader.line);
        }
 }
 
@@ -442,17 +443,23 @@ static struct discovery *discover_refs(const char *service, int for_push)
                break;
        case HTTP_MISSING_TARGET:
                show_http_message(&type, &charset, &buffer);
-               die("repository '%s' not found", url.buf);
+               die(_("repository '%s' not found"),
+                   transport_anonymize_url(url.buf));
        case HTTP_NOAUTH:
                show_http_message(&type, &charset, &buffer);
-               die("Authentication failed for '%s'", url.buf);
+               die(_("Authentication failed for '%s'"),
+                   transport_anonymize_url(url.buf));
        default:
                show_http_message(&type, &charset, &buffer);
-               die("unable to access '%s': %s", url.buf, curl_errorstr);
+               die(_("unable to access '%s': %s"),
+                   transport_anonymize_url(url.buf), curl_errorstr);
        }
 
-       if (options.verbosity && !starts_with(refs_url.buf, url.buf))
-               warning(_("redirecting to %s"), url.buf);
+       if (options.verbosity && !starts_with(refs_url.buf, url.buf)) {
+               char *u = transport_anonymize_url(url.buf);
+               warning(_("redirecting to %s"), u);
+               free(u);
+       }
 
        last= xcalloc(1, sizeof(*last_discovery));
        last->service = xstrdup(service);
@@ -574,7 +581,7 @@ static int rpc_read_from_out(struct rpc_state *rpc, int options,
                switch (*status) {
                case PACKET_READ_EOF:
                        if (!(options & PACKET_READ_GENTLE_ON_EOF))
-                               die("shouldn't have EOF when not gentle on EOF");
+                               die(_("shouldn't have EOF when not gentle on EOF"));
                        break;
                case PACKET_READ_NORMAL:
                        set_packet_header(buf - 4, *appended);
@@ -654,7 +661,7 @@ static curlioerr rpc_ioctl(CURL *handle, int cmd, void *clientp)
                        rpc->pos = 0;
                        return CURLIOE_OK;
                }
-               error("unable to rewind rpc post data - try increasing http.postBuffer");
+               error(_("unable to rewind rpc post data - try increasing http.postBuffer"));
                return CURLIOE_FAILRESTART;
 
        default:
@@ -714,7 +721,7 @@ static int run_slot(struct active_request_slot *slot,
                                strbuf_addstr(&msg, curl_errorstr);
                        }
                }
-               error("RPC failed; %s", msg.buf);
+               error(_("RPC failed; %s"), msg.buf);
                strbuf_release(&msg);
        }
 
@@ -754,7 +761,7 @@ static curl_off_t xcurl_off_t(size_t len)
 {
        uintmax_t size = len;
        if (size > maximum_signed_value_of_type(curl_off_t))
-               die("cannot handle pushes this big");
+               die(_("cannot handle pushes this big"));
        return (curl_off_t)size;
 }
 
@@ -869,11 +876,11 @@ static int post_rpc(struct rpc_state *rpc, int flush_received)
 
                ret = git_deflate(&stream, Z_FINISH);
                if (ret != Z_STREAM_END)
-                       die("cannot deflate request; zlib deflate error %d", ret);
+                       die(_("cannot deflate request; zlib deflate error %d"), ret);
 
                ret = git_deflate_end_gently(&stream);
                if (ret != Z_OK)
-                       die("cannot deflate request; zlib end error %d", ret);
+                       die(_("cannot deflate request; zlib end error %d"), ret);
 
                gzip_size = stream.total_out;
 
@@ -1004,7 +1011,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
 
        ALLOC_ARRAY(targets, nr_heads);
        if (options.depth || options.deepen_since)
-               die("dumb http transport does not support shallow capabilities");
+               die(_("dumb http transport does not support shallow capabilities"));
        for (i = 0; i < nr_heads; i++)
                targets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid));
 
@@ -1018,7 +1025,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
                free(targets[i]);
        free(targets);
 
-       return ret ? error("fetch failed.") : 0;
+       return ret ? error(_("fetch failed.")) : 0;
 }
 
 static int fetch_git(struct discovery *heads,
@@ -1066,7 +1073,7 @@ static int fetch_git(struct discovery *heads,
        for (i = 0; i < nr_heads; i++) {
                struct ref *ref = to_fetch[i];
                if (!*ref->name)
-                       die("cannot fetch by sha1 over smart http");
+                       die(_("cannot fetch by sha1 over smart http"));
                packet_buf_write(&preamble, "%s %s\n",
                                 oid_to_hex(&ref->old_oid), ref->name);
        }
@@ -1109,13 +1116,13 @@ static void parse_fetch(struct strbuf *buf)
                        struct object_id old_oid;
 
                        if (get_oid_hex(p, &old_oid))
-                               die("protocol error: expected sha/ref, got %s'", p);
+                               die(_("protocol error: expected sha/ref, got %s'"), p);
                        if (p[GIT_SHA1_HEXSZ] == ' ')
                                name = p + GIT_SHA1_HEXSZ + 1;
                        else if (!p[GIT_SHA1_HEXSZ])
                                name = "";
                        else
-                               die("protocol error: expected sha/ref, got %s'", p);
+                               die(_("protocol error: expected sha/ref, got %s'"), p);
 
                        ref = alloc_ref(name);
                        oidcpy(&ref->old_oid, &old_oid);
@@ -1127,7 +1134,7 @@ static void parse_fetch(struct strbuf *buf)
                        to_fetch[nr_heads++] = ref;
                }
                else
-                       die("http transport does not support %s", buf->buf);
+                       die(_("http transport does not support %s"), buf->buf);
 
                strbuf_reset(buf);
                if (strbuf_getline_lf(buf, stdin) == EOF)
@@ -1163,7 +1170,7 @@ static int push_dav(int nr_spec, char **specs)
                argv_array_push(&child.args, specs[i]);
 
        if (run_command(&child))
-               die("git-http-push failed");
+               die(_("git-http-push failed"));
        return 0;
 }
 
@@ -1241,7 +1248,7 @@ static void parse_push(struct strbuf *buf)
                        specs[nr_spec++] = xstrdup(buf->buf + 5);
                }
                else
-                       die("http transport does not support %s", buf->buf);
+                       die(_("http transport does not support %s"), buf->buf);
 
                strbuf_reset(buf);
                if (strbuf_getline_lf(buf, stdin) == EOF)
@@ -1349,7 +1356,7 @@ int cmd_main(int argc, const char **argv)
 
        setup_git_directory_gently(&nongit);
        if (argc < 2) {
-               error("remote-curl: usage: git remote-curl <remote> [<url>]");
+               error(_("remote-curl: usage: git remote-curl <remote> [<url>]"));
                return 1;
        }
 
@@ -1381,14 +1388,14 @@ int cmd_main(int argc, const char **argv)
 
                if (strbuf_getline_lf(&buf, stdin) == EOF) {
                        if (ferror(stdin))
-                               error("remote-curl: error reading command stream from git");
+                               error(_("remote-curl: error reading command stream from git"));
                        return 1;
                }
                if (buf.len == 0)
                        break;
                if (starts_with(buf.buf, "fetch ")) {
                        if (nongit)
-                               die("remote-curl: fetch attempted without a local repo");
+                               die(_("remote-curl: fetch attempted without a local repo"));
                        parse_fetch(&buf);
 
                } else if (!strcmp(buf.buf, "list") || starts_with(buf.buf, "list ")) {
@@ -1428,7 +1435,7 @@ int cmd_main(int argc, const char **argv)
                        if (!stateless_connect(arg))
                                break;
                } else {
-                       error("remote-curl: unknown command '%s' from git", buf.buf);
+                       error(_("remote-curl: unknown command '%s' from git"), buf.buf);
                        return 1;
                }
                strbuf_reset(&buf);
index 695021d6fc751751e726c598d90d3ed632bc21cc..60553d829d822b39c811065b1fbb10df21a3bceb 100644 (file)
@@ -2689,6 +2689,10 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        if (revs->first_parent_only && revs->bisect)
                die(_("--first-parent is incompatible with --bisect"));
 
+       if (revs->line_level_traverse &&
+           (revs->diffopt.output_format & ~(DIFF_FORMAT_PATCH | DIFF_FORMAT_NO_OUTPUT)))
+               die(_("-L does not yet support diff formats besides -p and -s"));
+
        if (revs->expand_tabs_in_log < 0)
                revs->expand_tabs_in_log = revs->expand_tabs_in_log_default;
 
index 95dda23eee450d1d60a70434d254071c6c05146b..7f4056dbf42e88d412c8772a9ec9b0fa6a8a1746 100644 (file)
@@ -55,8 +55,7 @@ static GIT_PATH_FUNC(rebase_path, "rebase-merge")
  * file and written to the tail of 'done'.
  */
 GIT_PATH_FUNC(rebase_path_todo, "rebase-merge/git-rebase-todo")
-static GIT_PATH_FUNC(rebase_path_todo_backup,
-                    "rebase-merge/git-rebase-todo.backup")
+GIT_PATH_FUNC(rebase_path_todo_backup, "rebase-merge/git-rebase-todo.backup")
 
 /*
  * The rebase command lines that have already been processed. A line
@@ -384,8 +383,8 @@ static void print_advice(struct repository *r, int show_hint,
        }
 }
 
-int write_message(const void *buf, size_t len, const char *filename,
-                 int append_eol)
+static int write_message(const void *buf, size_t len, const char *filename,
+                        int append_eol)
 {
        struct lock_file msg_file = LOCK_INIT;
 
@@ -1513,32 +1512,6 @@ static int allow_empty(struct repository *r,
                return 1;
 }
 
-/*
- * Note that ordering matters in this enum. Not only must it match the mapping
- * below, it is also divided into several sections that matter.  When adding
- * new commands, make sure you add it in the right section.
- */
-enum todo_command {
-       /* commands that handle commits */
-       TODO_PICK = 0,
-       TODO_REVERT,
-       TODO_EDIT,
-       TODO_REWORD,
-       TODO_FIXUP,
-       TODO_SQUASH,
-       /* commands that do something else than handling a single commit */
-       TODO_EXEC,
-       TODO_BREAK,
-       TODO_LABEL,
-       TODO_RESET,
-       TODO_MERGE,
-       /* commands that do nothing but are counted for reporting progress */
-       TODO_NOOP,
-       TODO_DROP,
-       /* comments (not counted for reporting progress) */
-       TODO_COMMENT
-};
-
 static struct {
        char c;
        const char *str;
@@ -2021,26 +1994,7 @@ enum todo_item_flags {
        TODO_EDIT_MERGE_MSG = 1
 };
 
-struct todo_item {
-       enum todo_command command;
-       struct commit *commit;
-       unsigned int flags;
-       const char *arg;
-       int arg_len;
-       size_t offset_in_buf;
-};
-
-struct todo_list {
-       struct strbuf buf;
-       struct todo_item *items;
-       int nr, alloc, current;
-       int done_nr, total_nr;
-       struct stat_data stat;
-};
-
-#define TODO_LIST_INIT { STRBUF_INIT }
-
-static void todo_list_release(struct todo_list *todo_list)
+void todo_list_release(struct todo_list *todo_list)
 {
        strbuf_release(&todo_list->buf);
        FREE_AND_NULL(todo_list->items);
@@ -2053,8 +2007,14 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)
        return todo_list->items + todo_list->nr++;
 }
 
+const char *todo_item_get_arg(struct todo_list *todo_list,
+                             struct todo_item *item)
+{
+       return todo_list->buf.buf + item->arg_offset;
+}
+
 static int parse_insn_line(struct repository *r, struct todo_item *item,
-                          const char *bol, char *eol)
+                          const char *buf, const char *bol, char *eol)
 {
        struct object_id commit_oid;
        char *end_of_object_name;
@@ -2068,7 +2028,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        if (bol == eol || *bol == '\r' || *bol == comment_line_char) {
                item->command = TODO_COMMENT;
                item->commit = NULL;
-               item->arg = bol;
+               item->arg_offset = bol - buf;
                item->arg_len = eol - bol;
                return 0;
        }
@@ -2095,7 +2055,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
                        return error(_("%s does not accept arguments: '%s'"),
                                     command_to_string(item->command), bol);
                item->commit = NULL;
-               item->arg = bol;
+               item->arg_offset = bol - buf;
                item->arg_len = eol - bol;
                return 0;
        }
@@ -2107,7 +2067,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        if (item->command == TODO_EXEC || item->command == TODO_LABEL ||
            item->command == TODO_RESET) {
                item->commit = NULL;
-               item->arg = bol;
+               item->arg_offset = bol - buf;
                item->arg_len = (int)(eol - bol);
                return 0;
        }
@@ -2121,7 +2081,7 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
                } else {
                        item->flags |= TODO_EDIT_MERGE_MSG;
                        item->commit = NULL;
-                       item->arg = bol;
+                       item->arg_offset = bol - buf;
                        item->arg_len = (int)(eol - bol);
                        return 0;
                }
@@ -2133,23 +2093,27 @@ static int parse_insn_line(struct repository *r, struct todo_item *item,
        status = get_oid(bol, &commit_oid);
        *end_of_object_name = saved;
 
-       item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
-       item->arg_len = (int)(eol - item->arg);
+       bol = end_of_object_name + strspn(end_of_object_name, " \t");
+       item->arg_offset = bol - buf;
+       item->arg_len = (int)(eol - bol);
 
        if (status < 0)
-               return -1;
+               return error(_("could not parse '%.*s'"),
+                            (int)(end_of_object_name - bol), bol);
 
        item->commit = lookup_commit_reference(r, &commit_oid);
        return !item->commit;
 }
 
-static int parse_insn_buffer(struct repository *r, char *buf,
-                            struct todo_list *todo_list)
+int todo_list_parse_insn_buffer(struct repository *r, char *buf,
+                               struct todo_list *todo_list)
 {
        struct todo_item *item;
        char *p = buf, *next_p;
        int i, res = 0, fixup_okay = file_exists(rebase_path_done());
 
+       todo_list->current = todo_list->nr = 0;
+
        for (i = 1; *p; i++, p = next_p) {
                char *eol = strchrnul(p, '\n');
 
@@ -2160,10 +2124,13 @@ static int parse_insn_buffer(struct repository *r, char *buf,
 
                item = append_new_todo(todo_list);
                item->offset_in_buf = p - todo_list->buf.buf;
-               if (parse_insn_line(r, item, p, eol)) {
+               if (parse_insn_line(r, item, buf, p, eol)) {
                        res = error(_("invalid line %d: %.*s"),
                                i, (int)(eol - p), p);
-                       item->command = TODO_NOOP;
+                       item->command = TODO_COMMENT + 1;
+                       item->arg_offset = p - buf;
+                       item->arg_len = (int)(eol - p);
+                       item->commit = NULL;
                }
 
                if (fixup_okay)
@@ -2238,7 +2205,7 @@ static int read_populate_todo(struct repository *r,
                return error(_("could not stat '%s'"), todo_file);
        fill_stat_data(&todo_list->stat, &st);
 
-       res = parse_insn_buffer(r, todo_list->buf.buf, todo_list);
+       res = todo_list_parse_insn_buffer(r, todo_list->buf.buf, todo_list);
        if (res) {
                if (is_rebase_i(opts))
                        return error(_("please fix this using "
@@ -2269,7 +2236,7 @@ static int read_populate_todo(struct repository *r,
                FILE *f = fopen_or_warn(rebase_path_msgtotal(), "w");
 
                if (strbuf_read_file(&done.buf, rebase_path_done(), 0) > 0 &&
-                   !parse_insn_buffer(r, done.buf.buf, &done))
+                   !todo_list_parse_insn_buffer(r, done.buf.buf, &done))
                        todo_list->done_nr = count_commands(&done);
                else
                        todo_list->done_nr = 0;
@@ -2506,7 +2473,7 @@ static int walk_revs_populate_todo(struct todo_list *todo_list,
 
                item->command = command;
                item->commit = commit;
-               item->arg = NULL;
+               item->arg_offset = 0;
                item->arg_len = 0;
                item->offset_in_buf = todo_list->buf.len;
                subject_len = find_commit_subject(commit_buffer, &subject);
@@ -3545,6 +3512,8 @@ static int pick_commits(struct repository *r,
 
        while (todo_list->current < todo_list->nr) {
                struct todo_item *item = todo_list->items + todo_list->current;
+               const char *arg = todo_item_get_arg(todo_list, item);
+
                if (save_todo(todo_list, opts))
                        return -1;
                if (is_rebase_i(opts)) {
@@ -3597,10 +3566,9 @@ static int pick_commits(struct repository *r,
                                        fprintf(stderr,
                                                _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
-                                               item->arg_len, item->arg);
+                                               item->arg_len, arg);
                                return error_with_patch(r, commit,
-                                       item->arg, item->arg_len, opts, res,
-                                       !res);
+                                       arg, item->arg_len, opts, res, !res);
                        }
                        if (is_rebase_i(opts) && !res)
                                record_in_rewritten(&item->commit->object.oid,
@@ -3609,7 +3577,7 @@ static int pick_commits(struct repository *r,
                                if (res == 1)
                                        intend_to_amend();
                                return error_failed_squash(r, item->commit, opts,
-                                       item->arg_len, item->arg);
+                                       item->arg_len, arg);
                        } else if (res && is_rebase_i(opts) && item->commit) {
                                int to_amend = 0;
                                struct object_id oid;
@@ -3628,19 +3596,18 @@ static int pick_commits(struct repository *r,
                                        to_amend = 1;
 
                                return res | error_with_patch(r, item->commit,
-                                               item->arg, item->arg_len, opts,
+                                               arg, item->arg_len, opts,
                                                res, to_amend);
                        }
                } else if (item->command == TODO_EXEC) {
-                       char *end_of_arg = (char *)(item->arg + item->arg_len);
+                       char *end_of_arg = (char *)(arg + item->arg_len);
                        int saved = *end_of_arg;
                        struct stat st;
 
                        *end_of_arg = '\0';
-                       res = do_exec(r, item->arg);
+                       res = do_exec(r, arg);
                        *end_of_arg = saved;
 
-                       /* Reread the todo file if it has changed. */
                        if (res) {
                                if (opts->reschedule_failed_exec)
                                        reschedule = 1;
@@ -3648,6 +3615,7 @@ static int pick_commits(struct repository *r,
                                res = error_errno(_("could not stat '%s'"),
                                                  get_todo_path(opts));
                        else if (match_stat_data(&todo_list->stat, &st)) {
+                               /* Reread the todo file if it has changed. */
                                todo_list_release(todo_list);
                                if (read_populate_todo(r, todo_list, opts))
                                        res = -1; /* message was printed */
@@ -3655,14 +3623,14 @@ static int pick_commits(struct repository *r,
                                todo_list->current = -1;
                        }
                } else if (item->command == TODO_LABEL) {
-                       if ((res = do_label(r, item->arg, item->arg_len)))
+                       if ((res = do_label(r, arg, item->arg_len)))
                                reschedule = 1;
                } else if (item->command == TODO_RESET) {
-                       if ((res = do_reset(r, item->arg, item->arg_len, opts)))
+                       if ((res = do_reset(r, arg, item->arg_len, opts)))
                                reschedule = 1;
                } else if (item->command == TODO_MERGE) {
                        if ((res = do_merge(r, item->commit,
-                                           item->arg, item->arg_len,
+                                           arg, item->arg_len,
                                            item->flags, opts)) < 0)
                                reschedule = 1;
                        else if (item->commit)
@@ -3671,9 +3639,8 @@ static int pick_commits(struct repository *r,
                        if (res > 0)
                                /* failed with merge conflicts */
                                return error_with_patch(r, item->commit,
-                                                       item->arg,
-                                                       item->arg_len, opts,
-                                                       res, 0);
+                                                       arg, item->arg_len,
+                                                       opts, res, 0);
                } else if (!is_noop(item->command))
                        return error(_("unknown command %d"), item->command);
 
@@ -3688,9 +3655,8 @@ static int pick_commits(struct repository *r,
                        if (item->commit)
                                return error_with_patch(r,
                                                        item->commit,
-                                                       item->arg,
-                                                       item->arg_len, opts,
-                                                       res, 0);
+                                                       arg, item->arg_len,
+                                                       opts, res, 0);
                }
 
                todo_list->current++;
@@ -4267,7 +4233,7 @@ static const char *label_oid(struct object_id *oid, const char *label,
 }
 
 static int make_script_with_merges(struct pretty_print_context *pp,
-                                  struct rev_info *revs, FILE *out,
+                                  struct rev_info *revs, struct strbuf *out,
                                   unsigned flags)
 {
        int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
@@ -4412,7 +4378,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
         * gathering commits not yet shown, reversing the list on the fly,
         * then outputting that list (labeling revisions as needed).
         */
-       fprintf(out, "%s onto\n", cmd_label);
+       strbuf_addf(out, "%s onto\n", cmd_label);
        for (iter = tips; iter; iter = iter->next) {
                struct commit_list *list = NULL, *iter2;
 
@@ -4422,9 +4388,9 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                entry = oidmap_get(&state.commit2label, &commit->object.oid);
 
                if (entry)
-                       fprintf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
+                       strbuf_addf(out, "\n%c Branch %s\n", comment_line_char, entry->string);
                else
-                       fprintf(out, "\n");
+                       strbuf_addch(out, '\n');
 
                while (oidset_contains(&interesting, &commit->object.oid) &&
                       !oidset_contains(&shown, &commit->object.oid)) {
@@ -4437,8 +4403,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                }
 
                if (!commit)
-                       fprintf(out, "%s %s\n", cmd_reset,
-                               rebase_cousins ? "onto" : "[new root]");
+                       strbuf_addf(out, "%s %s\n", cmd_reset,
+                                   rebase_cousins ? "onto" : "[new root]");
                else {
                        const char *to = NULL;
 
@@ -4451,12 +4417,12 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                                               &state);
 
                        if (!to || !strcmp(to, "onto"))
-                               fprintf(out, "%s onto\n", cmd_reset);
+                               strbuf_addf(out, "%s onto\n", cmd_reset);
                        else {
                                strbuf_reset(&oneline);
                                pretty_print_commit(pp, commit, &oneline);
-                               fprintf(out, "%s %s # %s\n",
-                                       cmd_reset, to, oneline.buf);
+                               strbuf_addf(out, "%s %s # %s\n",
+                                           cmd_reset, to, oneline.buf);
                        }
                }
 
@@ -4465,11 +4431,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,
                        entry = oidmap_get(&commit2todo, oid);
                        /* only show if not already upstream */
                        if (entry)
-                               fprintf(out, "%s\n", entry->string);
+                               strbuf_addf(out, "%s\n", entry->string);
                        entry = oidmap_get(&state.commit2label, oid);
                        if (entry)
-                               fprintf(out, "%s %s\n",
-                                       cmd_label, entry->string);
+                               strbuf_addf(out, "%s %s\n",
+                                           cmd_label, entry->string);
                        oidset_insert(&shown, oid);
                }
 
@@ -4491,13 +4457,11 @@ static int make_script_with_merges(struct pretty_print_context *pp,
        return 0;
 }
 
-int sequencer_make_script(struct repository *r, FILE *out,
-                         int argc, const char **argv,
-                         unsigned flags)
+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
+                         const char **argv, unsigned flags)
 {
        char *format = NULL;
        struct pretty_print_context pp = {0};
-       struct strbuf buf = STRBUF_INIT;
        struct rev_info revs;
        struct commit *commit;
        int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
@@ -4540,16 +4504,13 @@ int sequencer_make_script(struct repository *r, FILE *out,
 
                if (!is_empty && (commit->object.flags & PATCHSAME))
                        continue;
-               strbuf_reset(&buf);
                if (!keep_empty && is_empty)
-                       strbuf_addf(&buf, "%c ", comment_line_char);
-               strbuf_addf(&buf, "%s %s ", insn,
+                       strbuf_addf(out, "%c ", comment_line_char);
+               strbuf_addf(out, "%s %s ", insn,
                            oid_to_hex(&commit->object.oid));
-               pretty_print_commit(&pp, commit, &buf);
-               strbuf_addch(&buf, '\n');
-               fputs(buf.buf, out);
+               pretty_print_commit(&pp, commit, out);
+               strbuf_addch(out, '\n');
        }
-       strbuf_release(&buf);
        return 0;
 }
 
@@ -4557,91 +4518,95 @@ int sequencer_make_script(struct repository *r, FILE *out,
  * Add commands after pick and (series of) squash/fixup commands
  * in the todo list.
  */
-int sequencer_add_exec_commands(struct repository *r,
-                               const char *commands)
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+                                struct string_list *commands)
 {
-       const char *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT;
-       struct strbuf *buf = &todo_list.buf;
-       size_t offset = 0, commands_len = strlen(commands);
-       int i, insert;
+       struct strbuf *buf = &todo_list->buf;
+       size_t base_offset = buf->len;
+       int i, insert, nr = 0, alloc = 0;
+       struct todo_item *items = NULL, *base_items = NULL;
+
+       base_items = xcalloc(commands->nr, sizeof(struct todo_item));
+       for (i = 0; i < commands->nr; i++) {
+               size_t command_len = strlen(commands->items[i].string);
 
-       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-               return error(_("could not read '%s'."), todo_file);
+               strbuf_addstr(buf, commands->items[i].string);
+               strbuf_addch(buf, '\n');
 
-       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
-               todo_list_release(&todo_list);
-               return error(_("unusable todo list: '%s'"), todo_file);
+               base_items[i].command = TODO_EXEC;
+               base_items[i].offset_in_buf = base_offset;
+               base_items[i].arg_offset = base_offset + strlen("exec ");
+               base_items[i].arg_len = command_len - strlen("exec ");
+
+               base_offset += command_len + 1;
        }
 
        /*
         * Insert <commands> after every pick. Here, fixup/squash chains
         * are considered part of the pick, so we insert the commands *after*
         * those chains if there are any.
+        *
+        * As we insert the exec commands immediatly after rearranging
+        * any fixups and before the user edits the list, a fixup chain
+        * can never contain comments (any comments are empty picks that
+        * have been commented out because the user did not specify
+        * --keep-empty).  So, it is safe to insert an exec command
+        * without looking at the command following a comment.
         */
-       insert = -1;
-       for (i = 0; i < todo_list.nr; i++) {
-               enum todo_command command = todo_list.items[i].command;
-
-               if (insert >= 0) {
-                       /* skip fixup/squash chains */
-                       if (command == TODO_COMMENT)
-                               continue;
-                       else if (is_fixup(command)) {
-                               insert = i + 1;
-                               continue;
-                       }
-                       strbuf_insert(buf,
-                                     todo_list.items[insert].offset_in_buf +
-                                     offset, commands, commands_len);
-                       offset += commands_len;
-                       insert = -1;
+       insert = 0;
+       for (i = 0; i < todo_list->nr; i++) {
+               enum todo_command command = todo_list->items[i].command;
+               if (insert && !is_fixup(command)) {
+                       ALLOC_GROW(items, nr + commands->nr, alloc);
+                       COPY_ARRAY(items + nr, base_items, commands->nr);
+                       nr += commands->nr;
+
+                       insert = 0;
                }
 
+               ALLOC_GROW(items, nr + 1, alloc);
+               items[nr++] = todo_list->items[i];
+
                if (command == TODO_PICK || command == TODO_MERGE)
-                       insert = i + 1;
+                       insert = 1;
        }
 
        /* insert or append final <commands> */
-       if (insert >= 0 && insert < todo_list.nr)
-               strbuf_insert(buf, todo_list.items[insert].offset_in_buf +
-                             offset, commands, commands_len);
-       else if (insert >= 0 || !offset)
-               strbuf_add(buf, commands, commands_len);
+       if (insert || nr == todo_list->nr) {
+               ALLOC_GROW(items, nr + commands->nr, alloc);
+               COPY_ARRAY(items + nr, base_items, commands->nr);
+               nr += commands->nr;
+       }
 
-       i = write_message(buf->buf, buf->len, todo_file, 0);
-       todo_list_release(&todo_list);
-       return i;
+       free(base_items);
+       FREE_AND_NULL(todo_list->items);
+       todo_list->items = items;
+       todo_list->nr = nr;
+       todo_list->alloc = alloc;
 }
 
-int transform_todos(struct repository *r, unsigned flags)
+static void todo_list_to_strbuf(struct repository *r, struct todo_list *todo_list,
+                               struct strbuf *buf, int num, unsigned flags)
 {
-       const char *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT;
-       struct strbuf buf = STRBUF_INIT;
        struct todo_item *item;
-       int i;
-
-       if (strbuf_read_file(&todo_list.buf, todo_file, 0) < 0)
-               return error(_("could not read '%s'."), todo_file);
+       int i, max = todo_list->nr;
 
-       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list)) {
-               todo_list_release(&todo_list);
-               return error(_("unusable todo list: '%s'"), todo_file);
-       }
+       if (num > 0 && num < max)
+               max = num;
 
-       for (item = todo_list.items, i = 0; i < todo_list.nr; i++, item++) {
+       for (item = todo_list->items, i = 0; i < max; i++, item++) {
                /* if the item is not a command write it and continue */
                if (item->command >= TODO_COMMENT) {
-                       strbuf_addf(&buf, "%.*s\n", item->arg_len, item->arg);
+                       strbuf_addf(buf, "%.*s\n", item->arg_len,
+                                   todo_item_get_arg(todo_list, item));
                        continue;
                }
 
                /* add command to the buffer */
                if (flags & TODO_LIST_ABBREVIATE_CMDS)
-                       strbuf_addch(&buf, command_to_char(item->command));
+                       strbuf_addch(buf, command_to_char(item->command));
                else
-                       strbuf_addstr(&buf, command_to_string(item->command));
+                       strbuf_addstr(buf, command_to_string(item->command));
 
                /* add commit id */
                if (item->commit) {
@@ -4651,181 +4616,92 @@ int transform_todos(struct repository *r, unsigned flags)
 
                        if (item->command == TODO_MERGE) {
                                if (item->flags & TODO_EDIT_MERGE_MSG)
-                                       strbuf_addstr(&buf, " -c");
+                                       strbuf_addstr(buf, " -c");
                                else
-                                       strbuf_addstr(&buf, " -C");
+                                       strbuf_addstr(buf, " -C");
                        }
 
-                       strbuf_addf(&buf, " %s", oid);
+                       strbuf_addf(buf, " %s", oid);
                }
 
                /* add all the rest */
                if (!item->arg_len)
-                       strbuf_addch(&buf, '\n');
+                       strbuf_addch(buf, '\n');
                else
-                       strbuf_addf(&buf, " %.*s\n", item->arg_len, item->arg);
+                       strbuf_addf(buf, " %.*s\n", item->arg_len,
+                                   todo_item_get_arg(todo_list, item));
        }
-
-       i = write_message(buf.buf, buf.len, todo_file, 0);
-       todo_list_release(&todo_list);
-       return i;
 }
 
-enum missing_commit_check_level get_missing_commit_check_level(void)
+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
+                           const char *file, const char *shortrevisions,
+                           const char *shortonto, int num, unsigned flags)
 {
-       const char *value;
+       int res;
+       struct strbuf buf = STRBUF_INIT;
 
-       if (git_config_get_value("rebase.missingcommitscheck", &value) ||
-                       !strcasecmp("ignore", value))
-               return MISSING_COMMIT_CHECK_IGNORE;
-       if (!strcasecmp("warn", value))
-               return MISSING_COMMIT_CHECK_WARN;
-       if (!strcasecmp("error", value))
-               return MISSING_COMMIT_CHECK_ERROR;
-       warning(_("unrecognized setting %s for option "
-                 "rebase.missingCommitsCheck. Ignoring."), value);
-       return MISSING_COMMIT_CHECK_IGNORE;
-}
+       todo_list_to_strbuf(r, todo_list, &buf, num, flags);
+       if (flags & TODO_LIST_APPEND_TODO_HELP)
+               append_todo_help(flags & TODO_LIST_KEEP_EMPTY, count_commands(todo_list),
+                                shortrevisions, shortonto, &buf);
 
-define_commit_slab(commit_seen, unsigned char);
-/*
- * Check if the user dropped some commits by mistake
- * Behaviour determined by rebase.missingCommitsCheck.
- * Check if there is an unrecognized command or a
- * bad SHA-1 in a command.
- */
-int check_todo_list(struct repository *r)
-{
-       enum missing_commit_check_level check_level = get_missing_commit_check_level();
-       struct strbuf todo_file = STRBUF_INIT;
-       struct todo_list todo_list = TODO_LIST_INIT;
-       struct strbuf missing = STRBUF_INIT;
-       int advise_to_edit_todo = 0, res = 0, i;
-       struct commit_seen commit_seen;
+       res = write_message(buf.buf, buf.len, file, 0);
+       strbuf_release(&buf);
 
-       init_commit_seen(&commit_seen);
+       return res;
+}
 
-       strbuf_addstr(&todo_file, rebase_path_todo());
-       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
-               res = -1;
-               goto leave_check;
-       }
-       advise_to_edit_todo = res =
-               parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
+static const char edit_todo_list_advice[] =
+N_("You can fix this with 'git rebase --edit-todo' "
+"and then run 'git rebase --continue'.\n"
+"Or you can abort the rebase with 'git rebase"
+" --abort'.\n");
 
-       if (res || check_level == MISSING_COMMIT_CHECK_IGNORE)
-               goto leave_check;
+int check_todo_list_from_file(struct repository *r)
+{
+       struct todo_list old_todo = TODO_LIST_INIT, new_todo = TODO_LIST_INIT;
+       int res = 0;
 
-       /* Mark the commits in git-rebase-todo as seen */
-       for (i = 0; i < todo_list.nr; i++) {
-               struct commit *commit = todo_list.items[i].commit;
-               if (commit)
-                       *commit_seen_at(&commit_seen, commit) = 1;
+       if (strbuf_read_file_or_whine(&new_todo.buf, rebase_path_todo()) < 0) {
+               res = -1;
+               goto out;
        }
 
-       todo_list_release(&todo_list);
-       strbuf_addstr(&todo_file, ".backup");
-       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file.buf) < 0) {
+       if (strbuf_read_file_or_whine(&old_todo.buf, rebase_path_todo_backup()) < 0) {
                res = -1;
-               goto leave_check;
-       }
-       strbuf_release(&todo_file);
-       res = !!parse_insn_buffer(r, todo_list.buf.buf, &todo_list);
-
-       /* Find commits in git-rebase-todo.backup yet unseen */
-       for (i = todo_list.nr - 1; i >= 0; i--) {
-               struct todo_item *item = todo_list.items + i;
-               struct commit *commit = item->commit;
-               if (commit && !*commit_seen_at(&commit_seen, commit)) {
-                       strbuf_addf(&missing, " - %s %.*s\n",
-                                   short_commit_name(commit),
-                                   item->arg_len, item->arg);
-                       *commit_seen_at(&commit_seen, commit) = 1;
-               }
+               goto out;
        }
 
-       /* Warn about missing commits */
-       if (!missing.len)
-               goto leave_check;
-
-       if (check_level == MISSING_COMMIT_CHECK_ERROR)
-               advise_to_edit_todo = res = 1;
-
-       fprintf(stderr,
-               _("Warning: some commits may have been dropped accidentally.\n"
-               "Dropped commits (newer to older):\n"));
-
-       /* Make the list user-friendly and display */
-       fputs(missing.buf, stderr);
-       strbuf_release(&missing);
-
-       fprintf(stderr, _("To avoid this message, use \"drop\" to "
-               "explicitly remove a commit.\n\n"
-               "Use 'git config rebase.missingCommitsCheck' to change "
-               "the level of warnings.\n"
-               "The possible behaviours are: ignore, warn, error.\n\n"));
-
-leave_check:
-       clear_commit_seen(&commit_seen);
-       strbuf_release(&todo_file);
-       todo_list_release(&todo_list);
-
-       if (advise_to_edit_todo)
-               fprintf(stderr,
-                       _("You can fix this with 'git rebase --edit-todo' "
-                         "and then run 'git rebase --continue'.\n"
-                         "Or you can abort the rebase with 'git rebase"
-                         " --abort'.\n"));
+       res = todo_list_parse_insn_buffer(r, old_todo.buf.buf, &old_todo);
+       if (!res)
+               res = todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo);
+       if (!res)
+               res = todo_list_check(&old_todo, &new_todo);
+       if (res)
+               fprintf(stderr, _(edit_todo_list_advice));
+out:
+       todo_list_release(&old_todo);
+       todo_list_release(&new_todo);
 
        return res;
 }
 
-static int rewrite_file(const char *path, const char *buf, size_t len)
-{
-       int rc = 0;
-       int fd = open(path, O_WRONLY | O_TRUNC);
-       if (fd < 0)
-               return error_errno(_("could not open '%s' for writing"), path);
-       if (write_in_full(fd, buf, len) < 0)
-               rc = error_errno(_("could not write to '%s'"), path);
-       if (close(fd) && !rc)
-               rc = error_errno(_("could not close '%s'"), path);
-       return rc;
-}
-
 /* skip picking commits whose parents are unchanged */
-static int skip_unnecessary_picks(struct repository *r, struct object_id *output_oid)
+static int skip_unnecessary_picks(struct repository *r,
+                                 struct todo_list *todo_list,
+                                 struct object_id *base_oid)
 {
-       const char *todo_file = rebase_path_todo();
-       struct strbuf buf = STRBUF_INIT;
-       struct todo_list todo_list = TODO_LIST_INIT;
        struct object_id *parent_oid;
-       int fd, i;
-
-       if (!read_oneliner(&buf, rebase_path_onto(), 0))
-               return error(_("could not read 'onto'"));
-       if (get_oid(buf.buf, output_oid)) {
-               strbuf_release(&buf);
-               return error(_("need a HEAD to fixup"));
-       }
-       strbuf_release(&buf);
-
-       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
-               return -1;
-       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
-               todo_list_release(&todo_list);
-               return -1;
-       }
+       int i;
 
-       for (i = 0; i < todo_list.nr; i++) {
-               struct todo_item *item = todo_list.items + i;
+       for (i = 0; i < todo_list->nr; i++) {
+               struct todo_item *item = todo_list->items + i;
 
                if (item->command >= TODO_NOOP)
                        continue;
                if (item->command != TODO_PICK)
                        break;
                if (parse_commit(item->commit)) {
-                       todo_list_release(&todo_list);
                        return error(_("could not parse commit '%s'"),
                                oid_to_hex(&item->commit->object.oid));
                }
@@ -4834,135 +4710,101 @@ static int skip_unnecessary_picks(struct repository *r, struct object_id *output
                if (item->commit->parents->next)
                        break; /* merge commit */
                parent_oid = &item->commit->parents->item->object.oid;
-               if (!oideq(parent_oid, output_oid))
+               if (!oideq(parent_oid, base_oid))
                        break;
-               oidcpy(output_oid, &item->commit->object.oid);
+               oidcpy(base_oid, &item->commit->object.oid);
        }
        if (i > 0) {
-               int offset = get_item_line_offset(&todo_list, i);
                const char *done_path = rebase_path_done();
 
-               fd = open(done_path, O_CREAT | O_WRONLY | O_APPEND, 0666);
-               if (fd < 0) {
-                       error_errno(_("could not open '%s' for writing"),
-                                   done_path);
-                       todo_list_release(&todo_list);
-                       return -1;
-               }
-               if (write_in_full(fd, todo_list.buf.buf, offset) < 0) {
+               if (todo_list_write_to_file(r, todo_list, done_path, NULL, NULL, i, 0)) {
                        error_errno(_("could not write to '%s'"), done_path);
-                       todo_list_release(&todo_list);
-                       close(fd);
                        return -1;
                }
-               close(fd);
 
-               if (rewrite_file(rebase_path_todo(), todo_list.buf.buf + offset,
-                                todo_list.buf.len - offset) < 0) {
-                       todo_list_release(&todo_list);
-                       return -1;
-               }
+               MOVE_ARRAY(todo_list->items, todo_list->items + i, todo_list->nr - i);
+               todo_list->nr -= i;
+               todo_list->current = 0;
 
-               todo_list.current = i;
-               if (is_fixup(peek_command(&todo_list, 0)))
-                       record_in_rewritten(output_oid, peek_command(&todo_list, 0));
+               if (is_fixup(peek_command(todo_list, 0)))
+                       record_in_rewritten(base_oid, peek_command(todo_list, 0));
        }
 
-       todo_list_release(&todo_list);
-
        return 0;
 }
 
 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
-                   const char *onto, const char *orig_head, const char *cmd,
-                   unsigned autosquash)
+                   const char *onto, const char *orig_head, struct string_list *commands,
+                   unsigned autosquash, struct todo_list *todo_list)
 {
        const char *shortonto, *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT;
-       struct strbuf *buf = &(todo_list.buf);
+       struct todo_list new_todo = TODO_LIST_INIT;
+       struct strbuf *buf = &todo_list->buf;
        struct object_id oid;
-       struct stat st;
+       int res;
 
        get_oid(onto, &oid);
        shortonto = find_unique_abbrev(&oid, DEFAULT_ABBREV);
 
-       if (!lstat(todo_file, &st) && st.st_size == 0 &&
-           write_message("noop\n", 5, todo_file, 0))
-               return -1;
+       if (buf->len == 0) {
+               struct todo_item *item = append_new_todo(todo_list);
+               item->command = TODO_NOOP;
+               item->commit = NULL;
+               item->arg_len = item->arg_offset = item->flags = item->offset_in_buf = 0;
+       }
 
-       if (autosquash && rearrange_squash(r))
+       if (autosquash && todo_list_rearrange_squash(todo_list))
                return -1;
 
-       if (cmd && *cmd)
-               sequencer_add_exec_commands(r, cmd);
-
-       if (strbuf_read_file(buf, todo_file, 0) < 0)
-               return error_errno(_("could not read '%s'."), todo_file);
-
-       if (parse_insn_buffer(r, buf->buf, &todo_list)) {
-               todo_list_release(&todo_list);
-               return error(_("unusable todo list: '%s'"), todo_file);
-       }
+       if (commands->nr)
+               todo_list_add_exec_commands(todo_list, commands);
 
-       if (count_commands(&todo_list) == 0) {
+       if (count_commands(todo_list) == 0) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
-               todo_list_release(&todo_list);
 
                return error(_("nothing to do"));
        }
 
-       strbuf_addch(buf, '\n');
-       strbuf_commented_addf(buf, Q_("Rebase %s onto %s (%d command)",
-                                     "Rebase %s onto %s (%d commands)",
-                                     count_commands(&todo_list)),
-                             shortrevisions, shortonto, count_commands(&todo_list));
-       append_todo_help(0, flags & TODO_LIST_KEEP_EMPTY, buf);
-
-       if (write_message(buf->buf, buf->len, todo_file, 0)) {
-               todo_list_release(&todo_list);
+       res = edit_todo_list(r, todo_list, &new_todo, shortrevisions,
+                            shortonto, flags);
+       if (res == -1)
                return -1;
-       }
-
-       if (copy_file(rebase_path_todo_backup(), todo_file, 0666))
-               return error(_("could not copy '%s' to '%s'."), todo_file,
-                            rebase_path_todo_backup());
-
-       if (transform_todos(r, flags | TODO_LIST_SHORTEN_IDS))
-               return error(_("could not transform the todo list"));
-
-       strbuf_reset(buf);
-
-       if (launch_sequence_editor(todo_file, buf, NULL)) {
+       else if (res == -2) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
-               todo_list_release(&todo_list);
 
                return -1;
-       }
-
-       strbuf_stripspace(buf, 1);
-       if (buf->len == 0) {
+       } else if (res == -3) {
                apply_autostash(opts);
                sequencer_remove_state(opts);
-               todo_list_release(&todo_list);
+               todo_list_release(&new_todo);
 
                return error(_("nothing to do"));
        }
 
-       todo_list_release(&todo_list);
-
-       if (check_todo_list(r)) {
+       if (todo_list_parse_insn_buffer(r, new_todo.buf.buf, &new_todo) ||
+           todo_list_check(todo_list, &new_todo)) {
+               fprintf(stderr, _(edit_todo_list_advice));
                checkout_onto(opts, onto_name, onto, orig_head);
+               todo_list_release(&new_todo);
+
                return -1;
        }
 
-       if (transform_todos(r, flags & ~(TODO_LIST_SHORTEN_IDS)))
-               return error(_("could not transform the todo list"));
-
-       if (opts->allow_ff && skip_unnecessary_picks(r, &oid))
+       if (opts->allow_ff && skip_unnecessary_picks(r, &new_todo, &oid)) {
+               todo_list_release(&new_todo);
                return error(_("could not skip unnecessary pick commands"));
+       }
+
+       if (todo_list_write_to_file(r, &new_todo, todo_file, NULL, NULL, -1,
+                                   flags & ~(TODO_LIST_SHORTEN_IDS))) {
+               todo_list_release(&new_todo);
+               return error_errno(_("could not write '%s'"), todo_file);
+       }
+
+       todo_list_release(&new_todo);
 
        if (checkout_onto(opts, onto_name, oid_to_hex(&oid), orig_head))
                return -1;
@@ -4997,21 +4839,13 @@ define_commit_slab(commit_todo_item, struct todo_item *);
  * message will have to be retrieved from the commit (as the oneline in the
  * script cannot be trusted) in order to normalize the autosquash arrangement.
  */
-int rearrange_squash(struct repository *r)
+int todo_list_rearrange_squash(struct todo_list *todo_list)
 {
-       const char *todo_file = rebase_path_todo();
-       struct todo_list todo_list = TODO_LIST_INIT;
        struct hashmap subject2item;
-       int res = 0, rearranged = 0, *next, *tail, i;
+       int rearranged = 0, *next, *tail, i, nr = 0, alloc = 0;
        char **subjects;
        struct commit_todo_item commit_todo;
-
-       if (strbuf_read_file_or_whine(&todo_list.buf, todo_file) < 0)
-               return -1;
-       if (parse_insn_buffer(r, todo_list.buf.buf, &todo_list) < 0) {
-               todo_list_release(&todo_list);
-               return -1;
-       }
+       struct todo_item *items = NULL;
 
        init_commit_todo_item(&commit_todo);
        /*
@@ -5024,13 +4858,13 @@ int rearrange_squash(struct repository *r)
         * be moved to appear after the i'th.
         */
        hashmap_init(&subject2item, (hashmap_cmp_fn) subject2item_cmp,
-                    NULL, todo_list.nr);
-       ALLOC_ARRAY(next, todo_list.nr);
-       ALLOC_ARRAY(tail, todo_list.nr);
-       ALLOC_ARRAY(subjects, todo_list.nr);
-       for (i = 0; i < todo_list.nr; i++) {
+                    NULL, todo_list->nr);
+       ALLOC_ARRAY(next, todo_list->nr);
+       ALLOC_ARRAY(tail, todo_list->nr);
+       ALLOC_ARRAY(subjects, todo_list->nr);
+       for (i = 0; i < todo_list->nr; i++) {
                struct strbuf buf = STRBUF_INIT;
-               struct todo_item *item = todo_list.items + i;
+               struct todo_item *item = todo_list->items + i;
                const char *commit_buffer, *subject, *p;
                size_t subject_len;
                int i2 = -1;
@@ -5043,7 +4877,6 @@ int rearrange_squash(struct repository *r)
                }
 
                if (is_fixup(item->command)) {
-                       todo_list_release(&todo_list);
                        clear_commit_todo_item(&commit_todo);
                        return error(_("the script was already rearranged."));
                }
@@ -5078,7 +4911,7 @@ int rearrange_squash(struct repository *r)
                                 *commit_todo_item_at(&commit_todo, commit2))
                                /* found by commit name */
                                i2 = *commit_todo_item_at(&commit_todo, commit2)
-                                       - todo_list.items;
+                                       - todo_list->items;
                        else {
                                /* copy can be a prefix of the commit subject */
                                for (i2 = 0; i2 < i; i2++)
@@ -5091,7 +4924,7 @@ int rearrange_squash(struct repository *r)
                }
                if (i2 >= 0) {
                        rearranged = 1;
-                       todo_list.items[i].command =
+                       todo_list->items[i].command =
                                starts_with(subject, "fixup!") ?
                                TODO_FIXUP : TODO_SQUASH;
                        if (next[i2] < 0)
@@ -5109,10 +4942,8 @@ int rearrange_squash(struct repository *r)
        }
 
        if (rearranged) {
-               struct strbuf buf = STRBUF_INIT;
-
-               for (i = 0; i < todo_list.nr; i++) {
-                       enum todo_command command = todo_list.items[i].command;
+               for (i = 0; i < todo_list->nr; i++) {
+                       enum todo_command command = todo_list->items[i].command;
                        int cur = i;
 
                        /*
@@ -5123,37 +4954,26 @@ int rearrange_squash(struct repository *r)
                                continue;
 
                        while (cur >= 0) {
-                               const char *bol =
-                                       get_item_line(&todo_list, cur);
-                               const char *eol =
-                                       get_item_line(&todo_list, cur + 1);
-
-                               /* replace 'pick', by 'fixup' or 'squash' */
-                               command = todo_list.items[cur].command;
-                               if (is_fixup(command)) {
-                                       strbuf_addstr(&buf,
-                                               todo_command_info[command].str);
-                                       bol += strcspn(bol, " \t");
-                               }
-
-                               strbuf_add(&buf, bol, eol - bol);
-
+                               ALLOC_GROW(items, nr + 1, alloc);
+                               items[nr++] = todo_list->items[cur];
                                cur = next[cur];
                        }
                }
 
-               res = rewrite_file(todo_file, buf.buf, buf.len);
-               strbuf_release(&buf);
+               FREE_AND_NULL(todo_list->items);
+               todo_list->items = items;
+               todo_list->nr = nr;
+               todo_list->alloc = alloc;
        }
 
        free(next);
        free(tail);
-       for (i = 0; i < todo_list.nr; i++)
+       for (i = 0; i < todo_list->nr; i++)
                free(subjects[i]);
        free(subjects);
        hashmap_free(&subject2item, 1);
-       todo_list_release(&todo_list);
 
        clear_commit_todo_item(&commit_todo);
-       return res;
+
+       return 0;
 }
index 4d505b3590ed158600844cfe3889112b5da4e511..a515ee44578e6d8a568544749278c67feb4d233a 100644 (file)
@@ -10,6 +10,7 @@ struct repository;
 const char *git_path_commit_editmsg(void);
 const char *git_path_seq_dir(void);
 const char *rebase_path_todo(void);
+const char *rebase_path_todo_backup(void);
 
 #define APPEND_SIGNOFF_DEDUP (1u << 0)
 
@@ -66,14 +67,60 @@ struct replay_opts {
 };
 #define REPLAY_OPTS_INIT { .action = -1, .current_fixups = STRBUF_INIT }
 
-enum missing_commit_check_level {
-       MISSING_COMMIT_CHECK_IGNORE = 0,
-       MISSING_COMMIT_CHECK_WARN,
-       MISSING_COMMIT_CHECK_ERROR
+/*
+ * Note that ordering matters in this enum. Not only must it match the mapping
+ * of todo_command_info (in sequencer.c), it is also divided into several
+ * sections that matter.  When adding new commands, make sure you add it in the
+ * right section.
+ */
+enum todo_command {
+       /* commands that handle commits */
+       TODO_PICK = 0,
+       TODO_REVERT,
+       TODO_EDIT,
+       TODO_REWORD,
+       TODO_FIXUP,
+       TODO_SQUASH,
+       /* commands that do something else than handling a single commit */
+       TODO_EXEC,
+       TODO_BREAK,
+       TODO_LABEL,
+       TODO_RESET,
+       TODO_MERGE,
+       /* commands that do nothing but are counted for reporting progress */
+       TODO_NOOP,
+       TODO_DROP,
+       /* comments (not counted for reporting progress) */
+       TODO_COMMENT
 };
 
-int write_message(const void *buf, size_t len, const char *filename,
-                 int append_eol);
+struct todo_item {
+       enum todo_command command;
+       struct commit *commit;
+       unsigned int flags;
+       int arg_len;
+       /* The offset of the command and its argument in the strbuf */
+       size_t offset_in_buf, arg_offset;
+};
+
+struct todo_list {
+       struct strbuf buf;
+       struct todo_item *items;
+       int nr, alloc, current;
+       int done_nr, total_nr;
+       struct stat_data stat;
+};
+
+#define TODO_LIST_INIT { STRBUF_INIT }
+
+int todo_list_parse_insn_buffer(struct repository *r, char *buf,
+                               struct todo_list *todo_list);
+int todo_list_write_to_file(struct repository *r, struct todo_list *todo_list,
+                           const char *file, const char *shortrevisions,
+                           const char *shortonto, int num, unsigned flags);
+void todo_list_release(struct todo_list *todo_list);
+const char *todo_item_get_arg(struct todo_list *todo_list,
+                             struct todo_item *item);
 
 /* Call this to setup defaults before parsing command line options */
 void sequencer_init_config(struct replay_opts *opts);
@@ -93,19 +140,19 @@ int sequencer_remove_state(struct replay_opts *opts);
  * commits should be rebased onto the new base, this flag needs to be passed.
  */
 #define TODO_LIST_REBASE_COUSINS (1U << 4)
-int sequencer_make_script(struct repository *repo, FILE *out,
-                         int argc, const char **argv,
-                         unsigned flags);
-
-int sequencer_add_exec_commands(struct repository *r, const char *command);
-int transform_todos(struct repository *r, unsigned flags);
-enum missing_commit_check_level get_missing_commit_check_level(void);
-int check_todo_list(struct repository *r);
+#define TODO_LIST_APPEND_TODO_HELP (1U << 5)
+
+int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
+                         const char **argv, unsigned flags);
+
+void todo_list_add_exec_commands(struct todo_list *todo_list,
+                                struct string_list *commands);
+int check_todo_list_from_file(struct repository *r);
 int complete_action(struct repository *r, struct replay_opts *opts, unsigned flags,
                    const char *shortrevisions, const char *onto_name,
-                   const char *onto, const char *orig_head, const char *cmd,
-                   unsigned autosquash);
-int rearrange_squash(struct repository *r);
+                   const char *onto, const char *orig_head, struct string_list *commands,
+                   unsigned autosquash, struct todo_list *todo_list);
+int todo_list_rearrange_squash(struct todo_list *todo_list);
 
 /*
  * Append a signoff to the commit message in "msgbuf". The ignore_footer
index 6dda2c16df1026e50b7d9be255b6bbfc4e92725c..07c71a7567012aedc7faeccfa5c89cf2363d42c0 100644 (file)
@@ -442,6 +442,18 @@ static enum get_oid_result get_short_oid(const char *name, int len,
        find_short_packed_object(&ds);
        status = finish_object_disambiguation(&ds, oid);
 
+       /*
+        * If we didn't find it, do the usual reprepare() slow-path,
+        * since the object may have recently been added to the repository
+        * or migrated from loose to packed.
+        */
+       if (status == MISSING_OBJECT) {
+               reprepare_packed_git(the_repository);
+               find_short_object_filename(&ds);
+               find_short_packed_object(&ds);
+               status = finish_object_disambiguation(&ds, oid);
+       }
+
        if (!quietly && (status == SHORT_NAME_AMBIGUOUS)) {
                struct oid_array collect = OID_ARRAY_INIT;
 
@@ -1518,6 +1530,25 @@ int get_oid(const char *name, struct object_id *oid)
        return get_oid_with_context(the_repository, name, 0, oid, &unused);
 }
 
+/*
+ * This returns a non-zero value if the string (built using printf
+ * format and the given arguments) is not a valid object.
+ */
+int get_oidf(struct object_id *oid, const char *fmt, ...)
+{
+       va_list ap;
+       int ret;
+       struct strbuf sb = STRBUF_INIT;
+
+       va_start(ap, fmt);
+       strbuf_vaddf(&sb, fmt, ap);
+       va_end(ap);
+
+       ret = get_oid(sb.buf, oid);
+       strbuf_release(&sb);
+
+       return ret;
+}
 
 /*
  * Many callers know that the user meant to name a commit-ish by
index 232357eb2ea0397388254a4b188333a227bf5b10..16033998da4b273aebd92c84b1e1b12e4aaf7009 160000 (submodule)
@@ -1 +1 @@
-Subproject commit 232357eb2ea0397388254a4b188333a227bf5b10
+Subproject commit 16033998da4b273aebd92c84b1e1b12e4aaf7009
index df0630bc6d607749f80955bc9ffaa5b0bbc506a5..5931cf25d518ac3bfc30c3b2adb8ddfc0ccbf187 100644 (file)
 #endif
 /*ENDIANNESS SELECTION*/
 
+#ifndef SHA1DC_FORCE_ALIGNED_ACCESS
 #if defined(SHA1DC_FORCE_UNALIGNED_ACCESS) || defined(SHA1DC_ON_INTEL_LIKE_PROCESSOR)
 #define SHA1DC_ALLOW_UNALIGNED_ACCESS
-#endif /*UNALIGNMENT DETECTION*/
-
+#endif /*UNALIGNED ACCESS DETECTION*/
+#endif /*FORCE ALIGNED ACCESS*/
 
 #define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n))))
 #define rotate_left(x,n)  (((x)<<(n))|((x)>>(32-(n))))
index 78eecd29f7e64109203af17cd6e7c2f7e95e3dfc..0e18b259ce51fc73f82f7b77032221b33411c25f 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -249,6 +249,42 @@ void strbuf_insert(struct strbuf *sb, size_t pos, const void *data, size_t len)
        strbuf_splice(sb, pos, 0, data, len);
 }
 
+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt, va_list ap)
+{
+       int len, len2;
+       char save;
+       va_list cp;
+
+       if (pos > sb->len)
+               die("`pos' is too far after the end of the buffer");
+       va_copy(cp, ap);
+       len = vsnprintf(sb->buf + sb->len, 0, fmt, cp);
+       va_end(cp);
+       if (len < 0)
+               BUG("your vsnprintf is broken (returned %d)", len);
+       if (!len)
+               return; /* nothing to do */
+       if (unsigned_add_overflows(sb->len, len))
+               die("you want to use way too much memory");
+       strbuf_grow(sb, len);
+       memmove(sb->buf + pos + len, sb->buf + pos, sb->len - pos);
+       /* vsnprintf() will append a NUL, overwriting one of our characters */
+       save = sb->buf[pos + len];
+       len2 = vsnprintf(sb->buf + pos, len + 1, fmt, ap);
+       sb->buf[pos + len] = save;
+       if (len2 != len)
+               BUG("your vsnprintf is broken (returns inconsistent lengths)");
+       strbuf_setlen(sb, sb->len + len);
+}
+
+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...)
+{
+       va_list ap;
+       va_start(ap, fmt);
+       strbuf_vinsertf(sb, pos, fmt, ap);
+       va_end(ap);
+}
+
 void strbuf_remove(struct strbuf *sb, size_t pos, size_t len)
 {
        strbuf_splice(sb, pos, len, "", 0);
@@ -268,6 +304,21 @@ void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2)
        strbuf_setlen(sb, sb->len + sb2->len);
 }
 
+const char *strbuf_join_argv(struct strbuf *buf,
+                            int argc, const char **argv, char delim)
+{
+       if (!argc)
+               return buf->buf;
+
+       strbuf_addstr(buf, *argv);
+       while (--argc) {
+               strbuf_addch(buf, delim);
+               strbuf_addstr(buf, *(++argv));
+       }
+
+       return buf->buf;
+}
+
 void strbuf_addchars(struct strbuf *sb, int c, size_t n)
 {
        strbuf_grow(sb, n);
index 52e44c9ab83b3409f4f861db41a138123ba433b1..c8d98dfb95b8d81b316e887fa031dab0b2165784 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -244,6 +244,15 @@ void strbuf_addchars(struct strbuf *sb, int c, size_t n);
  */
 void strbuf_insert(struct strbuf *sb, size_t pos, const void *, size_t);
 
+/**
+ * Insert data to the given position of the buffer giving a printf format
+ * string. The contents will be shifted, not overwritten.
+ */
+void strbuf_vinsertf(struct strbuf *sb, size_t pos, const char *fmt,
+                    va_list ap);
+
+void strbuf_insertf(struct strbuf *sb, size_t pos, const char *fmt, ...);
+
 /**
  * Remove given amount of data from a given position of the buffer.
  */
@@ -288,6 +297,13 @@ static inline void strbuf_addstr(struct strbuf *sb, const char *s)
  */
 void strbuf_addbuf(struct strbuf *sb, const struct strbuf *sb2);
 
+/**
+ * Join the arguments into a buffer. `delim` is put between every
+ * two arguments.
+ */
+const char *strbuf_join_argv(struct strbuf *buf, int argc,
+                            const char **argv, char delim);
+
 /**
  * This function can be used to expand a format string containing
  * placeholders. To that end, it parses the string and calls the specified
index 21cf50ca15e124edb67b4aa76ae87577816eb1a5..b16c0ecc950c177e9f6bd0c553d593b08855a478 100644 (file)
@@ -1548,6 +1548,13 @@ static int fetch_finish(int retvalue, struct strbuf *err,
        struct oid_array *commits;
 
        if (retvalue)
+               /*
+                * NEEDSWORK: This indicates that the overall fetch
+                * failed, even though there may be a subsequent fetch
+                * by commit hash that might work. It may be a good
+                * idea to not indicate failure in this case, and only
+                * indicate failure if the subsequent fetch fails.
+                */
                spf->result = 1;
 
        if (!task || !task->sub)
index 656288edcea5a23a5893ade5c2d81ee429eb26e8..6404f33e19421c47999518138ed6f46a066921c0 100644 (file)
--- a/t/README
+++ b/t/README
@@ -343,6 +343,9 @@ marked strings" in po/README for details.
 GIT_TEST_SPLIT_INDEX=<boolean> forces split-index mode on the whole
 test suite. Accept any boolean values that are accepted by git-config.
 
+GIT_TEST_PROTOCOL_VERSION=<n>, when set, overrides the
+'protocol.version' setting to n if it is less than n.
+
 GIT_TEST_FULL_IN_PACK_ARRAY=<boolean> exercises the uncommon
 pack-objects code path where there are more than 1024 packs even if
 the actual number of packs in repository is below this limit. Accept
@@ -381,8 +384,8 @@ the --no-sparse command-line argument.
 GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
 by overriding the minimum number of cache entries required per thread.
 
-GIT_TEST_REBASE_USE_BUILTIN=<boolean>, when false, disables the
-builtin version of git-rebase. See 'rebase.useBuiltin' in
+GIT_TEST_STASH_USE_BUILTIN=<boolean>, when false, disables the
+built-in version of git-stash. See 'stash.useBuiltin' in
 git-config(1).
 
 GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
@@ -399,6 +402,10 @@ GIT_TEST_SIDEBAND_ALL=<boolean>, when true, overrides the
 fetch-pack to not request sideband-all (even if the server advertises
 sideband-all).
 
+GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=<boolean>, when true (which is
+the default when running tests), errors out when an abbreviated option
+is used.
+
 Naming Tests
 ------------
 
@@ -864,6 +871,26 @@ library for your script to use.
                ...
        '
 
+ - test_atexit <script>
+
+   Prepend <script> to a list of commands to run unconditionally to
+   clean up before the test script exits, e.g. to stop a daemon:
+
+       test_expect_success 'test git daemon' '
+               git daemon &
+               daemon_pid=$! &&
+               test_atexit 'kill $daemon_pid' &&
+               hello world
+       '
+
+   The commands will be executed before the trash directory is removed,
+   i.e. the atexit commands will still be able to access any pidfiles or
+   socket files.
+
+   Note that these commands will be run even when a test script run
+   with '--immediate' fails.  Be careful with your atexit commands to
+   minimize any changes to the failed state.
+
  - test_write_lines <lines>
 
    Write <lines> on standard output, one line per argument.
index 1daf69420be1948cdc1eb97b4b4122e22674d43e..4d22e42f8422adac89c37ba4ef94f8a8ca66f413 100755 (executable)
@@ -37,5 +37,4 @@ test_expect_success "fetch with $VERSION_B" '
        test_cmp expect actual
 '
 
-stop_git_daemon
 test_done
index 79db3b7ae513c01b07422ed1a8d95f9f5b285cb5..7b3407134e1a776de7a030bce98f0248fe6cd42f 100644 (file)
@@ -13,7 +13,6 @@
 #
 #      test_expect_success ...
 #
-#      stop_git_daemon
 #      test_done
 
 test_tristate GIT_TEST_GIT_DAEMON
@@ -31,10 +30,12 @@ fi
 test_set_port LIB_GIT_DAEMON_PORT
 
 GIT_DAEMON_PID=
+GIT_DAEMON_PIDFILE="$PWD"/daemon.pid
 GIT_DAEMON_DOCUMENT_ROOT_PATH="$PWD"/repo
 GIT_DAEMON_HOST_PORT=127.0.0.1:$LIB_GIT_DAEMON_PORT
 GIT_DAEMON_URL=git://$GIT_DAEMON_HOST_PORT
 
+registered_stop_git_daemon_atexit_handler=
 start_git_daemon() {
        if test -n "$GIT_DAEMON_PID"
        then
@@ -43,13 +44,19 @@ start_git_daemon() {
 
        mkdir -p "$GIT_DAEMON_DOCUMENT_ROOT_PATH"
 
-       trap 'code=$?; stop_git_daemon; (exit $code); die' EXIT
+       # One of the test scripts stops and then re-starts 'git daemon'.
+       # Don't register and then run the same atexit handlers several times.
+       if test -z "$registered_stop_git_daemon_atexit_handler"
+       then
+               test_atexit 'stop_git_daemon'
+               registered_stop_git_daemon_atexit_handler=AlreadyDone
+       fi
 
        say >&3 "Starting git daemon ..."
        mkfifo git_daemon_output
        ${LIB_GIT_DAEMON_COMMAND:-git daemon} \
                --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
-               --reuseaddr --verbose \
+               --reuseaddr --verbose --pid-file="$GIT_DAEMON_PIDFILE" \
                --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
                "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
                >&3 2>git_daemon_output &
@@ -65,7 +72,7 @@ start_git_daemon() {
        then
                kill "$GIT_DAEMON_PID"
                wait "$GIT_DAEMON_PID"
-               trap 'die' EXIT
+               unset GIT_DAEMON_PID
                test_skip_or_die $GIT_TEST_GIT_DAEMON \
                        "git daemon failed to start"
        fi
@@ -77,8 +84,6 @@ stop_git_daemon() {
                return
        fi
 
-       trap 'die' EXIT
-
        # kill git-daemon child of git
        say >&3 "Stopping git daemon ..."
        kill "$GIT_DAEMON_PID"
@@ -88,8 +93,9 @@ stop_git_daemon() {
        then
                error "git daemon exited with status: $ret"
        fi
+       kill "$(cat "$GIT_DAEMON_PIDFILE")" 2>/dev/null
        GIT_DAEMON_PID=
-       rm -f git_daemon_output
+       rm -f git_daemon_output "$GIT_DAEMON_PIDFILE"
 }
 
 # A stripped-down version of a netcat client, that connects to a "host:port"
index b3be3ba011a71ce11f11901c8472cb25ec21133a..547b9f88e1235a9248f6da9d1fa24dffd7cb0cd3 100644 (file)
@@ -44,15 +44,6 @@ native_path () {
        echo "$path"
 }
 
-# On Solaris the 'date +%s' function is not supported and therefore we
-# need this replacement.
-# Attention: This function is not safe again against time offset updates
-# at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
-# function could fix that but it is not in Python until 3.3.
-time_in_seconds () {
-       (cd / && "$PYTHON_PATH" -c 'import time; print(int(time.time()))')
-}
-
 test_set_port P4DPORT
 
 P4PORT=localhost:$P4DPORT
@@ -67,14 +58,9 @@ cli="$TRASH_DIRECTORY/cli"
 git="$TRASH_DIRECTORY/git"
 pidfile="$TRASH_DIRECTORY/p4d.pid"
 
-# Sometimes "prove" seems to hang on exit because p4d is still running
-cleanup () {
-       if test -f "$pidfile"
-       then
-               kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
-       fi
+stop_p4d_and_watchdog () {
+       kill -9 $p4d_pid $watchdog_pid
 }
-trap cleanup EXIT
 
 # git p4 submit generates a temp file, which will
 # not get cleaned up if the submission fails.  Don't
@@ -82,7 +68,16 @@ trap cleanup EXIT
 TMPDIR="$TRASH_DIRECTORY"
 export TMPDIR
 
+registered_stop_p4d_atexit_handler=
 start_p4d () {
+       # One of the test scripts stops and then re-starts p4d.
+       # Don't register and then run the same atexit handlers several times.
+       if test -z "$registered_stop_p4d_atexit_handler"
+       then
+               test_atexit 'stop_p4d_and_watchdog'
+               registered_stop_p4d_atexit_handler=AlreadyDone
+       fi
+
        mkdir -p "$db" "$cli" "$git" &&
        rm -f "$pidfile" &&
        (
@@ -92,6 +87,7 @@ start_p4d () {
                        echo $! >"$pidfile"
                }
        ) &&
+       p4d_pid=$(cat "$pidfile")
 
        # This gives p4d a long time to start up, as it can be
        # quite slow depending on the machine.  Set this environment
@@ -99,18 +95,18 @@ start_p4d () {
        # an automated test setup.  If the p4d process dies, that
        # will be caught with the "kill -0" check below.
        i=${P4D_START_PATIENCE:-300}
-       pid=$(cat "$pidfile")
 
-       timeout=$(($(time_in_seconds) + $P4D_TIMEOUT))
+       nr_tries_left=$P4D_TIMEOUT
        while true
        do
-               if test $(time_in_seconds) -gt $timeout
+               if test $nr_tries_left -eq 0
                then
-                       kill -9 $pid
+                       kill -9 $p4d_pid
                        exit 1
                fi
                sleep 1
-       done &
+               nr_tries_left=$(($nr_tries_left - 1))
+       done 2>/dev/null 4>&2 &
        watchdog_pid=$!
 
        ready=
@@ -123,7 +119,7 @@ start_p4d () {
                        break
                fi
                # fail if p4d died
-               kill -0 $pid 2>/dev/null || break
+               kill -0 $p4d_pid 2>/dev/null || break
                echo waiting for p4d to start
                sleep 1
                i=$(( $i - 1 ))
@@ -163,29 +159,18 @@ p4_add_job () {
 }
 
 retry_until_success () {
-       timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
-       until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
-       do
-               sleep 1
-       done
-}
-
-retry_until_fail () {
-       timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
-       until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+       nr_tries_left=$RETRY_TIMEOUT
+       until "$@" 2>/dev/null || test $nr_tries_left -eq 0
        do
                sleep 1
+               nr_tries_left=$(($nr_tries_left - 1))
        done
 }
 
-kill_p4d () {
-       pid=$(cat "$pidfile")
-       retry_until_fail kill $pid
-       retry_until_fail kill -9 $pid
-       # complain if it would not die
-       test_must_fail kill $pid >/dev/null 2>&1 &&
-       rm -rf "$db" "$cli" "$pidfile" &&
-       retry_until_fail kill -9 $watchdog_pid
+stop_and_cleanup_p4d () {
+       kill -9 $p4d_pid $watchdog_pid
+       wait $p4d_pid
+       rm -rf "$db" "$cli" "$pidfile"
 }
 
 cleanup_git () {
index f3b478c307c99397fb180fb3d5688e1d6f257904..c1271d686372dfd46d640a84ebec3971933ed4f2 100644 (file)
@@ -76,11 +76,6 @@ maybe_start_httpd () {
                LIB_HTTPD_SVN="$loc"
                start_httpd
                ;;
-       *)
-               stop_httpd () {
-                       : noop
-               }
-               ;;
        esac
 }
 
index 0dfb48c2f6df78348c216bd34693758c0d22365d..b3cc62bd36f26f776b307607a941b01c547c404d 100644 (file)
@@ -14,7 +14,6 @@
 #
 #      test_expect_success ...
 #
-#      stop_httpd
 #      test_done
 #
 # Can be configured using the following variables.
@@ -176,7 +175,7 @@ prepare_httpd() {
 start_httpd() {
        prepare_httpd >&3 2>&4
 
-       trap 'code=$?; stop_httpd; (exit $code); die' EXIT
+       test_atexit stop_httpd
 
        "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
                -f "$TEST_PATH/apache.conf" $HTTPD_PARA \
@@ -184,15 +183,12 @@ start_httpd() {
                >&3 2>&4
        if test $? -ne 0
        then
-               trap 'die' EXIT
                cat "$HTTPD_ROOT_PATH"/error.log >&4 2>/dev/null
                test_skip_or_die $GIT_TEST_HTTPD "web server setup failed"
        fi
 }
 
 stop_httpd() {
-       trap 'die' EXIT
-
        "$LIB_HTTPD_PATH" -d "$HTTPD_ROOT_PATH" \
                -f "$TEST_PATH/apache.conf" $HTTPD_PARA -k stop
 }
index 2e33ab3ec3ef9838962c7a8b1a997faeed11f071..169f92eae3d419b2d9d4ccdfbde4a394cc9e15dd 100644 (file)
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see http://www.gnu.org/licenses/ .
 
-# do the --tee work early; it otherwise confuses our careful
-# GIT_BUILD_DIR mangling
-case "$GIT_TEST_TEE_STARTED, $* " in
-done,*)
-       # do not redirect again
-       ;;
-*' --tee '*|*' --va'*)
-       mkdir -p test-results
-       BASE=test-results/$(basename "$0" .sh)
-       (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
-        echo $? > $BASE.exit) | tee $BASE.out
-       test "$(cat $BASE.exit)" = 0
-       exit
-       ;;
-esac
-
+# These variables must be set before the inclusion of test-lib.sh below,
+# because it will change our working directory.
 TEST_DIRECTORY=$(pwd)/..
 TEST_OUTPUT_DIRECTORY=$(pwd)
-if test -z "$GIT_TEST_INSTALLED"; then
-       perf_results_prefix=
-else
-       perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
-       # make the tested dir absolute
-       GIT_TEST_INSTALLED=$(cd "$GIT_TEST_INSTALLED" && pwd)
-fi
+ABSOLUTE_GIT_TEST_INSTALLED=$(
+       test -n "$GIT_TEST_INSTALLED" && cd "$GIT_TEST_INSTALLED" && pwd)
 
 TEST_NO_CREATE_REPO=t
 TEST_NO_MALLOC_CHECK=t
 
 . ../test-lib.sh
 
+if test -z "$GIT_TEST_INSTALLED"; then
+       perf_results_prefix=
+else
+       perf_results_prefix=$(printf "%s" "${GIT_TEST_INSTALLED%/bin-wrappers}" | tr -c "[a-zA-Z0-9]" "[_*]")"."
+       GIT_TEST_INSTALLED=$ABSOLUTE_GIT_TEST_INSTALLED
+fi
+
 # Variables from test-lib that are normally internal to the tests; we
 # need to export them for test_perf subshells
 export TEST_DIRECTORY TRASH_DIRECTORY GIT_BUILD_DIR GIT_TEST_CMP
index b6566003dd8704503305314767f84d516c170f7a..c03054c538a0f9220cb268ed4fa24ae963d06e84 100755 (executable)
@@ -825,6 +825,24 @@ test_expect_success 'tests clean up even on failures' "
        EOF
 "
 
+test_expect_success 'test_atexit is run' "
+       test_must_fail run_sub_test_lib_test \
+               atexit-cleanup 'Run atexit commands' -i <<-\\EOF &&
+       test_expect_success 'tests clean up even after a failure' '
+               > ../../clean-atexit &&
+               test_atexit rm ../../clean-atexit &&
+               > ../../also-clean-atexit &&
+               test_atexit rm ../../also-clean-atexit &&
+               > ../../dont-clean-atexit &&
+               (exit 1)
+       '
+       test_done
+       EOF
+       test_path_is_file dont-clean-atexit &&
+       test_path_is_missing clean-atexit &&
+       test_path_is_missing also-clean-atexit
+"
+
 test_expect_success 'test_oid setup' '
        test_oid_init
 '
index 5e27604b24a7357575c1d51c7cafda9f5cb06471..1f462204eaf878694be8045313097155480cec57 100755 (executable)
@@ -454,6 +454,17 @@ test_expect_success 're-init from a linked worktree' '
        )
 '
 
+test_expect_success MINGW 'core.hidedotfiles = false' '
+       git config --global core.hidedotfiles false &&
+       rm -rf newdir &&
+       mkdir newdir &&
+       (
+               sane_unset GIT_DIR GIT_WORK_TREE GIT_CONFIG &&
+               git -C newdir init
+       ) &&
+       ! is_hidden newdir/.git
+'
+
 test_expect_success MINGW 'redirect std handles' '
        GIT_REDIRECT_STDOUT=output.txt git rev-parse --git-dir &&
        test .git = "$(cat output.txt)" &&
index b8f366c4420f47d913b1d830037d846e4e443074..800b3ea5f5b64650efa95bfa05e8f90945966f85 100755 (executable)
@@ -203,20 +203,24 @@ file: (not set)
 EOF
 
 test_expect_success 'unambiguously abbreviated option' '
+       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
        test-tool parse-options --int 2 --boolean --no-bo >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
 '
 
 test_expect_success 'unambiguously abbreviated option with "="' '
+       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
        test-tool parse-options --expect="integer: 2" --int=2
 '
 
 test_expect_success 'ambiguously abbreviated option' '
-       test_expect_code 129 test-tool parse-options --strin 123
+       test_expect_code 129 env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+       test-tool parse-options --strin 123
 '
 
 test_expect_success 'non ambiguous option (after two options it abbreviates)' '
+       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
        test-tool parse-options --expect="string: 123" --st 123
 '
 
@@ -325,6 +329,7 @@ file: (not set)
 EOF
 
 test_expect_success 'negation of OPT_NONEG flags is not ambiguous' '
+       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
        test-tool parse-options --no-ambig >output 2>output.err &&
        test_must_be_empty output.err &&
        test_cmp expect output
@@ -370,4 +375,11 @@ test_expect_success '--no-verbose resets multiple verbose to 0' '
        test-tool parse-options --expect="verbose: 0" -v -v -v --no-verbose
 '
 
+test_expect_success 'GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS works' '
+       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=false \
+               test-tool parse-options --ye &&
+       test_must_fail env GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true \
+               test-tool parse-options --ye
+'
+
 test_done
index fd92533acf488fd7d1aedd8152b9edaafc9bd87d..ebd5fa5249ca276b739977739e8dea27aa6728aa 100755 (executable)
@@ -10,7 +10,7 @@ test -z "$NO_UNIX_SOCKETS" || {
 }
 
 # don't leave a stale daemon running
-trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
+test_atexit 'git credential-cache exit'
 
 # test that the daemon works with no special setup
 helper_test cache
@@ -108,9 +108,4 @@ test_expect_success SYMLINKS 'use user socket if user directory is a symlink to
 
 helper_test_timeout cache --timeout=1
 
-# we can't rely on our "trap" above working after test_done,
-# as test_done will delete the trash directory containing
-# our socket, leaving us with no way to access the daemon.
-git credential-cache exit
-
 test_done
index bce02788e6e1b03b87911fb13594e0cf6f80fdab..5bd892f2f7a90ac9e35993c137e0addd10d17bde 100755 (executable)
@@ -518,6 +518,4 @@ test_expect_success 'fetching of missing objects from an HTTP server' '
        git verify-pack --verbose "$IDX" | grep "$HASH"
 '
 
-stop_httpd
-
 test_done
index 635918505d122f822dcaee54e16ee9669c81693c..579a86b7f8f6f876434bc1107e051304bc45afc6 100755 (executable)
@@ -229,6 +229,19 @@ test_expect_success 'conditional include, early config reading' '
        )
 '
 
+test_expect_success 'conditional include with /**/' '
+       REPO=foo/bar/repo &&
+       git init $REPO &&
+       cat >>$REPO/.git/config <<-\EOF &&
+       [includeIf "gitdir:**/foo/**/bar/**"]
+       path=bar7
+       EOF
+       echo "[test]seven=7" >$REPO/.git/bar7 &&
+       echo 7 >expect &&
+       git -C $REPO config test.seven >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success SYMLINKS 'conditional include, set up symlinked $HOME' '
        mkdir real-home &&
        ln -s real-home home &&
index 6b6a8e2292b5a37f12c3ea8ba2acd4825689c512..970c5c36b9b5a7138b6825499caf2b87303d845e 100755 (executable)
@@ -618,4 +618,20 @@ test_expect_success 'delete fails cleanly if packed-refs file is locked' '
        test_cmp unchanged actual
 '
 
+test_expect_success 'delete fails cleanly if packed-refs.new write fails' '
+       # Setup and expectations are similar to the test above.
+       prefix=refs/failed-packed-refs &&
+       git update-ref $prefix/foo $C &&
+       git pack-refs --all &&
+       git update-ref $prefix/foo $D &&
+       git for-each-ref $prefix >unchanged &&
+       # This should not happen in practice, but it is an easy way to get a
+       # reliable error (we open with create_tempfile(), which uses O_EXCL).
+       : >.git/packed-refs.new &&
+       test_when_finished "rm -f .git/packed-refs.new" &&
+       test_must_fail git update-ref -d $prefix/foo &&
+       git for-each-ref $prefix >actual &&
+       test_cmp unchanged actual
+'
+
 test_done
index b664e51250ae0fcd6b1520a2fa424f34aa6d0479..bb2c7572a3849821f0f6b26f83c1130bb572e04b 100755 (executable)
@@ -76,4 +76,39 @@ test_expect_success 'reflog of worktrees/xx/HEAD' '
        test_cmp expected actual.wt2
 '
 
+test_expect_success 'for-each-ref from main repo' '
+       mkdir fer1 &&
+       git -C fer1 init repo &&
+       test_commit -C fer1/repo initial &&
+       git -C fer1/repo worktree add ../second &&
+       git -C fer1/repo update-ref refs/bisect/main HEAD &&
+       git -C fer1/repo update-ref refs/rewritten/main HEAD &&
+       git -C fer1/repo update-ref refs/worktree/main HEAD &&
+       git -C fer1/repo for-each-ref --format="%(refname)" | grep main >actual &&
+       cat >expected <<-\EOF &&
+       refs/bisect/main
+       refs/rewritten/main
+       refs/worktree/main
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'for-each-ref from linked repo' '
+       mkdir fer2 &&
+       git -C fer2 init repo &&
+       test_commit -C fer2/repo initial &&
+       git -C fer2/repo worktree add ../second &&
+       git -C fer2/second update-ref refs/bisect/second HEAD &&
+       git -C fer2/second update-ref refs/rewritten/second HEAD &&
+       git -C fer2/second update-ref refs/worktree/second HEAD &&
+       git -C fer2/second for-each-ref --format="%(refname)" | grep second >actual &&
+       cat >expected <<-\EOF &&
+       refs/bisect/second
+       refs/heads/second
+       refs/rewritten/second
+       refs/worktree/second
+       EOF
+       test_cmp expected actual
+'
+
 test_done
index 7e189851348d571b2e654325ba38e7564d0bc950..fca3f858245781229ea29b4baeb86e356f50a571 100755 (executable)
@@ -46,4 +46,28 @@ test_expect_success '-m restores 3-way conflicted+resolved file' '
        test_cmp both.txt.conflicted.cleaned both.txt.cleaned
 '
 
+test_expect_success 'force checkout a conflict file creates stage zero entry' '
+       git init co-force &&
+       (
+               cd co-force &&
+               echo a >a &&
+               git add a &&
+               git commit -ama &&
+               A_OBJ=$(git rev-parse :a) &&
+               git branch topic &&
+               echo b >a &&
+               git commit -amb &&
+               B_OBJ=$(git rev-parse :a) &&
+               git checkout topic &&
+               echo c >a &&
+               C_OBJ=$(git hash-object a) &&
+               git checkout -m master &&
+               test_cmp_rev :1:a $A_OBJ &&
+               test_cmp_rev :2:a $B_OBJ &&
+               test_cmp_rev :3:a $C_OBJ &&
+               git checkout -f topic &&
+               test_cmp_rev :0:a $A_OBJ
+       )
+'
+
 test_done
index 460d0523be6f35e4477a010edd365c1f8fbd431c..42f147858d7c2e87d6920cdd77744e99bf77001b 100755 (executable)
@@ -319,4 +319,20 @@ test_expect_success 'rebase--merge.sh and --show-current-patch' '
        )
 '
 
+test_expect_success 'rebase -c rebase.useBuiltin=false warning' '
+       expected="rebase.useBuiltin support has been removed" &&
+
+       # Only warn when the legacy rebase is requested...
+       test_must_fail git -c rebase.useBuiltin=false rebase 2>err &&
+       test_i18ngrep "$expected" err &&
+       test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=false git rebase 2>err &&
+       test_i18ngrep "$expected" err &&
+
+       # ...not when we would have used the built-in anyway
+       test_must_fail git -c rebase.useBuiltin=true rebase 2>err &&
+       test_must_be_empty err &&
+       test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true git rebase 2>err &&
+       test_must_be_empty err
+'
+
 test_done
index b60b11f9f2f820bb7347a845eb66524528c668f9..1723e1a858585d9e83d7662326a6f337db7e1fa6 100755 (executable)
@@ -149,12 +149,10 @@ test_expect_success 'rebase -i with the exec command checks tree cleanness' '
 
 test_expect_success 'rebase -x with empty command fails' '
        test_when_finished "git rebase --abort ||:" &&
-       test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \
-               git rebase -x "" @ 2>actual &&
+       test_must_fail env git rebase -x "" @ 2>actual &&
        test_write_lines "error: empty exec command" >expected &&
        test_i18ncmp expected actual &&
-       test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \
-               git rebase -x " " @ 2>actual &&
+       test_must_fail env git rebase -x " " @ 2>actual &&
        test_i18ncmp expected actual
 '
 
@@ -162,8 +160,7 @@ LF='
 '
 test_expect_success 'rebase -x with newline in command fails' '
        test_when_finished "git rebase --abort ||:" &&
-       test_must_fail env GIT_TEST_REBASE_USE_BUILTIN=true \
-               git rebase -x "a${LF}b" @ 2>actual &&
+       test_must_fail env git rebase -x "a${LF}b" @ 2>actual &&
        test_write_lines "error: exec commands cannot contain newlines" \
                         >expected &&
        test_i18ncmp expected actual
index 13f5688135d853106702b7679eb302f6ae3d1450..22d218698e958add3126f40479ca559f52b2bda8 100755 (executable)
@@ -277,7 +277,7 @@ test_expect_success 'autosquash with empty custom instructionFormat' '
        (
                set_cat_todo_editor &&
                test_must_fail git -c rebase.instructionFormat= \
-                       rebase --autosquash  --force -i HEAD^ >actual &&
+                       rebase --autosquash  --force-rebase -i HEAD^ >actual &&
                git log -1 --format="pick %h %s" >expect &&
                test_cmp expect actual
        )
index b9292dfc2a329b32472368fb2874cc4a1e14cf47..76f6d306eaf39e9727bfeb3dbb04aa3c3941cf1d 100755 (executable)
@@ -11,4 +11,26 @@ test_expect_success 'rebase exec modifies rebase-todo' '
        test -e F
 '
 
+test_expect_success SHA1 'loose object cache vs re-reading todo list' '
+       GIT_REBASE_TODO=.git/rebase-merge/git-rebase-todo &&
+       export GIT_REBASE_TODO &&
+       write_script append-todo.sh <<-\EOS &&
+       # For values 5 and 6, this yields SHA-1s with the same first two digits
+       echo "pick $(git rev-parse --short \
+               $(printf "%s\\n" \
+                       "tree $EMPTY_TREE" \
+                       "author A U Thor <author@example.org> $1 +0000" \
+                       "committer A U Thor <author@example.org> $1 +0000" \
+                       "" \
+                       "$1" |
+                 git hash-object -t commit -w --stdin))" >>$GIT_REBASE_TODO
+
+       shift
+       test -z "$*" ||
+       echo "exec $0 $*" >>$GIT_REBASE_TODO
+       EOS
+
+       git rebase HEAD -x "./append-todo.sh 5 6"
+'
+
 test_done
index 4c69255ee6b14bdd52b6f840e2e528c182cb23ec..42ba5b9f0981b2b7ce98e1c5c55012ef61757bab 100755 (executable)
@@ -271,7 +271,7 @@ test_expect_success 'root commits' '
        EOF
        test_config sequence.editor \""$PWD"/replace-editor.sh\" &&
        test_tick &&
-       git rebase -i --force --root -r &&
+       git rebase -i --force-rebase --root -r &&
        test "Parsnip" = "$(git show -s --format=%an HEAD^)" &&
        test $(git rev-parse second-root^0) != $(git rev-parse HEAD^) &&
        test $(git rev-parse second-root:second-root.t) = \
@@ -364,7 +364,7 @@ test_expect_success 'octopus merges' '
        test_cmp_rev HEAD $before &&
 
        test_tick &&
-       git rebase -i --force -r HEAD^^ &&
+       git rebase -i --force-rebase -r HEAD^^ &&
        test "Hank" = "$(git show -s --format=%an HEAD)" &&
        test "$before" != $(git rev-parse HEAD) &&
        test_cmp_graph HEAD^^.. <<-\EOF
index 04e5d42bd3b82a7bf683659f6fdd8e20813df770..85ae7dc1e4618275413a8b498f43d43f3e0ead92 100755 (executable)
@@ -8,91 +8,92 @@ test_description='Test of the various options to git rm.'
 . ./test-lib.sh
 
 # Setup some files to be removed, some with funny characters
-test_expect_success \
-    'Initialize test directory' \
-    "touch -- foo bar baz 'space embedded' -q &&
-     git add -- foo bar baz 'space embedded' -q &&
-     git commit -m 'add normal files'"
+test_expect_success 'Initialize test directory' '
+       touch -- foo bar baz "space embedded" -q &&
+       git add -- foo bar baz "space embedded" -q &&
+       git commit -m "add normal files"
+'
 
-if test_have_prereq !FUNNYNAMES; then
+if test_have_prereq !FUNNYNAMES
+then
        say 'Your filesystem does not allow tabs in filenames.'
 fi
 
-test_expect_success FUNNYNAMES 'add files with funny names' "
-     touch -- 'tab     embedded' 'newline
-embedded' &&
-     git add -- 'tab   embedded' 'newline
-embedded' &&
-     git commit -m 'add files with tabs and newlines'
-"
-
-test_expect_success \
-    'Pre-check that foo exists and is in index before git rm foo' \
-    '[ -f foo ] && git ls-files --error-unmatch foo'
-
-test_expect_success \
-    'Test that git rm foo succeeds' \
-    'git rm --cached foo'
-
-test_expect_success \
-    'Test that git rm --cached foo succeeds if the index matches the file' \
-    'echo content >foo &&
-     git add foo &&
-     git rm --cached foo'
-
-test_expect_success \
-    'Test that git rm --cached foo succeeds if the index matches the file' \
-    'echo content >foo &&
-     git add foo &&
-     git commit -m foo &&
-     echo "other content" >foo &&
-     git rm --cached foo'
-
-test_expect_success \
-    'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
-     echo content >foo &&
-     git add foo &&
-     git commit -m foo --allow-empty &&
-     echo "other content" >foo &&
-     git add foo &&
-     echo "yet another content" >foo &&
-     test_must_fail git rm --cached foo
-'
-
-test_expect_success \
-    'Test that git rm --cached -f foo works in case where --cached only did not' \
-    'echo content >foo &&
-     git add foo &&
-     git commit -m foo --allow-empty &&
-     echo "other content" >foo &&
-     git add foo &&
-     echo "yet another content" >foo &&
-     git rm --cached -f foo'
-
-test_expect_success \
-    'Post-check that foo exists but is not in index after git rm foo' \
-    '[ -f foo ] && test_must_fail git ls-files --error-unmatch foo'
-
-test_expect_success \
-    'Pre-check that bar exists and is in index before "git rm bar"' \
-    '[ -f bar ] && git ls-files --error-unmatch bar'
-
-test_expect_success \
-    'Test that "git rm bar" succeeds' \
-    'git rm bar'
-
-test_expect_success \
-    'Post-check that bar does not exist and is not in index after "git rm -f bar"' \
-    '! [ -f bar ] && test_must_fail git ls-files --error-unmatch bar'
-
-test_expect_success \
-    'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' \
-    'git rm -- -q'
-
-test_expect_success FUNNYNAMES \
-    "Test that \"git rm -f\" succeeds with embedded space, tab, or newline characters." \
-    "git rm -f 'space embedded' 'tab   embedded' 'newline
-embedded'"
+test_expect_success FUNNYNAMES 'add files with funny names' '
+       touch -- "tab   embedded" "newline${LF}embedded" &&
+       git add -- "tab embedded" "newline${LF}embedded" &&
+       git commit -m "add files with tabs and newlines"
+'
+
+test_expect_success 'Pre-check that foo exists and is in index before git rm foo' '
+       test_path_is_file foo &&
+       git ls-files --error-unmatch foo
+'
+
+test_expect_success 'Test that git rm foo succeeds' '
+       git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached foo succeeds if the index matches the file' '
+       echo content >foo &&
+       git add foo &&
+       git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached foo succeeds if the index matches the file' '
+       echo content >foo &&
+       git add foo &&
+       git commit -m foo &&
+       echo "other content" >foo &&
+       git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached foo fails if the index matches neither the file nor HEAD' '
+       echo content >foo &&
+       git add foo &&
+       git commit -m foo --allow-empty &&
+       echo "other content" >foo &&
+       git add foo &&
+       echo "yet another content" >foo &&
+       test_must_fail git rm --cached foo
+'
+
+test_expect_success 'Test that git rm --cached -f foo works in case where --cached only did not' '
+       echo content >foo &&
+       git add foo &&
+       git commit -m foo --allow-empty &&
+       echo "other content" >foo &&
+       git add foo &&
+       echo "yet another content" >foo &&
+       git rm --cached -f foo
+'
+
+test_expect_success 'Post-check that foo exists but is not in index after git rm foo' '
+       test_path_is_file foo &&
+       test_must_fail git ls-files --error-unmatch foo
+'
+
+test_expect_success 'Pre-check that bar exists and is in index before "git rm bar"' '
+       test_path_is_file bar &&
+       git ls-files --error-unmatch bar
+'
+
+test_expect_success 'Test that "git rm bar" succeeds' '
+       git rm bar
+'
+
+test_expect_success 'Post-check that bar does not exist and is not in index after "git rm -f bar"' '
+       test_path_is_missing bar &&
+       test_must_fail git ls-files --error-unmatch bar
+'
+
+test_expect_success 'Test that "git rm -- -q" succeeds (remove a file that looks like an option)' '
+       git rm -- -q
+'
+
+test_expect_success FUNNYNAMES 'Test that "git rm -f" succeeds with embedded space, tab, or newline characters.' '
+       git rm -f "space embedded" "tab embedded" "newline${LF}embedded"
+'
 
 test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
        test_when_finished "chmod 775 ." &&
@@ -100,9 +101,9 @@ test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
        test_must_fail git rm -f baz
 '
 
-test_expect_success \
-    'When the rm in "git rm -f" fails, it should not remove the file from the index' \
-    'git ls-files --error-unmatch baz'
+test_expect_success 'When the rm in "git rm -f" fails, it should not remove the file from the index' '
+       git ls-files --error-unmatch baz
+'
 
 test_expect_success 'Remove nonexistent file with --ignore-unmatch' '
        git rm --ignore-unmatch nonexistent
@@ -137,15 +138,15 @@ test_expect_success 'Re-add foo and baz' '
 test_expect_success 'Modify foo -- rm should refuse' '
        echo >>foo &&
        test_must_fail git rm foo baz &&
-       test -f foo &&
-       test -f baz &&
+       test_path_is_file foo &&
+       test_path_is_file baz &&
        git ls-files --error-unmatch foo baz
 '
 
 test_expect_success 'Modified foo -- rm -f should work' '
        git rm -f foo baz &&
-       test ! -f foo &&
-       test ! -f baz &&
+       test_path_is_missing foo &&
+       test_path_is_missing baz &&
        test_must_fail git ls-files --error-unmatch foo &&
        test_must_fail git ls-files --error-unmatch bar
 '
@@ -159,15 +160,15 @@ test_expect_success 'Re-add foo and baz for HEAD tests' '
 
 test_expect_success 'foo is different in index from HEAD -- rm should refuse' '
        test_must_fail git rm foo baz &&
-       test -f foo &&
-       test -f baz &&
+       test_path_is_file foo &&
+       test_path_is_file baz &&
        git ls-files --error-unmatch foo baz
 '
 
 test_expect_success 'but with -f it should work.' '
        git rm -f foo baz &&
-       test ! -f foo &&
-       test ! -f baz &&
+       test_path_is_missing foo &&
+       test_path_is_missing baz &&
        test_must_fail git ls-files --error-unmatch foo &&
        test_must_fail git ls-files --error-unmatch baz
 '
@@ -194,21 +195,21 @@ test_expect_success 'Recursive test setup' '
 
 test_expect_success 'Recursive without -r fails' '
        test_must_fail git rm frotz &&
-       test -d frotz &&
-       test -f frotz/nitfol
+       test_path_is_dir frotz &&
+       test_path_is_file frotz/nitfol
 '
 
 test_expect_success 'Recursive with -r but dirty' '
        echo qfwfq >>frotz/nitfol &&
        test_must_fail git rm -r frotz &&
-       test -d frotz &&
-       test -f frotz/nitfol
+       test_path_is_dir frotz &&
+       test_path_is_file frotz/nitfol
 '
 
 test_expect_success 'Recursive with -r -f' '
        git rm -f -r frotz &&
-       ! test -f frotz/nitfol &&
-       ! test -d frotz
+       test_path_is_missing frotz/nitfol &&
+       test_path_is_missing frotz
 '
 
 test_expect_success 'Remove nonexistent file returns nonzero exit status' '
@@ -217,23 +218,25 @@ test_expect_success 'Remove nonexistent file returns nonzero exit status' '
 
 test_expect_success 'Call "rm" from outside the work tree' '
        mkdir repo &&
-       (cd repo &&
-        git init &&
-        echo something >somefile &&
-        git add somefile &&
-        git commit -m "add a file" &&
-        (cd .. &&
-         git --git-dir=repo/.git --work-tree=repo rm somefile) &&
-       test_must_fail git ls-files --error-unmatch somefile)
+       (
+               cd repo &&
+               git init &&
+               echo something >somefile &&
+               git add somefile &&
+               git commit -m "add a file" &&
+               (
+                       cd .. &&
+                       git --git-dir=repo/.git --work-tree=repo rm somefile
+               ) &&
+               test_must_fail git ls-files --error-unmatch somefile
+       )
 '
 
 test_expect_success 'refresh index before checking if it is up-to-date' '
-
        git reset --hard &&
        test-tool chmtime -86400 frotz/nitfol &&
        git rm frotz/nitfol &&
-       test ! -f frotz/nitfol
-
+       test_path_is_missing frotz/nitfol
 '
 
 test_expect_success 'choking "git rm" should not let it die with cruft' '
@@ -242,8 +245,8 @@ test_expect_success 'choking "git rm" should not let it die with cruft' '
        i=0 &&
        while test $i -lt 12000
        do
-           echo "100644 1234567890123456789012345678901234567890 0     some-file-$i"
-           i=$(( $i + 1 ))
+               echo "100644 1234567890123456789012345678901234567890 0 some-file-$i"
+               i=$(( $i + 1 ))
        done | git update-index --index-info &&
        git rm -n "some-file-*" | : &&
        test_path_is_missing .git/index.lock
@@ -254,7 +257,7 @@ test_expect_success 'rm removes subdirectories recursively' '
        echo content >dir/subdir/subsubdir/file &&
        git add dir/subdir/subsubdir/file &&
        git rm -f dir/subdir/subsubdir/file &&
-       ! test -d dir
+       test_path_is_missing dir
 '
 
 cat >expect <<EOF
@@ -292,7 +295,7 @@ test_expect_success 'rm removes empty submodules from work tree' '
        git add .gitmodules &&
        git commit -m "add submodule" &&
        git rm submod &&
-       test ! -e submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
@@ -314,7 +317,7 @@ test_expect_success 'rm removes work tree of unmodified submodules' '
        git reset --hard &&
        git submodule update &&
        git rm submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
@@ -325,7 +328,7 @@ test_expect_success 'rm removes a submodule with a trailing /' '
        git reset --hard &&
        git submodule update &&
        git rm submod/ &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -343,12 +346,12 @@ test_expect_success 'rm of a populated submodule with different HEAD fails unles
        git submodule update &&
        git -C submod checkout HEAD^ &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
@@ -359,8 +362,8 @@ test_expect_success 'rm --cached leaves work tree of populated submodules and .g
        git reset --hard &&
        git submodule update &&
        git rm --cached submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno >actual &&
        test_cmp expect.cached actual &&
        git config -f .gitmodules submodule.sub.url &&
@@ -371,7 +374,7 @@ test_expect_success 'rm --dry-run does not touch the submodule or .gitmodules' '
        git reset --hard &&
        git submodule update &&
        git rm -n submod &&
-       test -f submod/.git &&
+       test_path_is_file submod/.git &&
        git diff-index --exit-code HEAD
 '
 
@@ -381,8 +384,8 @@ test_expect_success 'rm does not complain when no .gitmodules file is found' '
        git rm .gitmodules &&
        git rm submod >actual 2>actual.err &&
        test_must_be_empty actual.err &&
-       ! test -d submod &&
-       ! test -f submod/.git &&
+       test_path_is_missing submod &&
+       test_path_is_missing submod/.git &&
        git status -s -uno >actual &&
        test_cmp expect.both_deleted actual
 '
@@ -392,15 +395,15 @@ test_expect_success 'rm will error out on a modified .gitmodules file unless sta
        git submodule update &&
        git config -f .gitmodules foo.bar true &&
        test_must_fail git rm submod >actual 2>actual.err &&
-       test -s actual.err &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_file_not_empty actual.err &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git diff-files --quiet -- submod &&
        git add .gitmodules &&
        git rm submod >actual 2>actual.err &&
        test_must_be_empty actual.err &&
-       ! test -d submod &&
-       ! test -f submod/.git &&
+       test_path_is_missing submod &&
+       test_path_is_missing submod/.git &&
        git status -s -uno >actual &&
        test_cmp expect actual
 '
@@ -413,8 +416,8 @@ test_expect_success 'rm issues a warning when section is not found in .gitmodule
        echo "warning: Could not find section in .gitmodules where path=submod" >expect.err &&
        git rm submod >actual 2>actual.err &&
        test_i18ncmp expect.err actual.err &&
-       ! test -d submod &&
-       ! test -f submod/.git &&
+       test_path_is_missing submod &&
+       test_path_is_missing submod/.git &&
        git status -s -uno >actual &&
        test_cmp expect actual
 '
@@ -424,12 +427,12 @@ test_expect_success 'rm of a populated submodule with modifications fails unless
        git submodule update &&
        echo X >submod/empty &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified_inside actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -439,12 +442,12 @@ test_expect_success 'rm of a populated submodule with untracked files fails unle
        git submodule update &&
        echo X >submod/untracked &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified_untracked actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -481,7 +484,7 @@ test_expect_success 'rm removes work tree of unmodified conflicted submodule' '
        git submodule update &&
        test_must_fail git merge conflict2 &&
        git rm submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -493,12 +496,12 @@ test_expect_success 'rm of a conflicted populated submodule with different HEAD
        git -C submod checkout HEAD^ &&
        test_must_fail git merge conflict2 &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
@@ -512,12 +515,12 @@ test_expect_success 'rm of a conflicted populated submodule with modifications f
        echo X >submod/empty &&
        test_must_fail git merge conflict2 &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual &&
        test_must_fail git config -f .gitmodules submodule.sub.url &&
@@ -531,12 +534,12 @@ test_expect_success 'rm of a conflicted populated submodule with untracked files
        echo X >submod/untracked &&
        test_must_fail git merge conflict2 &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -545,20 +548,21 @@ test_expect_success 'rm of a conflicted populated submodule with a .git director
        git checkout conflict1 &&
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
+       (
+               cd submod &&
                rm .git &&
                cp -R ../.git/modules/sub .git &&
                GIT_WORK_TREE=. git config --unset core.worktree
        ) &&
        test_must_fail git merge conflict2 &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -d submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_dir submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        test_must_fail git rm -f submod &&
-       test -d submod &&
-       test -d submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_dir submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.conflict actual &&
        git merge --abort &&
@@ -570,7 +574,7 @@ test_expect_success 'rm of a conflicted unpopulated submodule succeeds' '
        git reset --hard &&
        test_must_fail git merge conflict2 &&
        git rm submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -579,17 +583,18 @@ test_expect_success 'rm of a populated submodule with a .git directory migrates
        git checkout -f master &&
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
+       (
+               cd submod &&
                rm .git &&
                cp -R ../.git/modules/sub .git &&
                GIT_WORK_TREE=. git config --unset core.worktree &&
                rm -r ../.git/modules/sub
        ) &&
        git rm submod 2>output.err &&
-       ! test -d submod &&
-       ! test -d submod/.git &&
+       test_path_is_missing submod &&
+       test_path_is_missing submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test -s actual &&
+       test_file_not_empty actual &&
        test_i18ngrep Migrating output.err
 '
 
@@ -600,7 +605,8 @@ EOF
 test_expect_success 'setup subsubmodule' '
        git reset --hard &&
        git submodule update &&
-       (cd submod &&
+       (
+               cd submod &&
                git update-index --add --cacheinfo 160000 $(git rev-parse HEAD) subsubmod &&
                git config -f .gitmodules submodule.sub.url ../. &&
                git config -f .gitmodules submodule.sub.path subsubmod &&
@@ -614,7 +620,7 @@ test_expect_success 'setup subsubmodule' '
 
 test_expect_success 'rm recursively removes work tree of unmodified submodules' '
        git rm submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -624,12 +630,12 @@ test_expect_success 'rm of a populated nested submodule with different nested HE
        git submodule update --recursive &&
        git -C submod/subsubmod checkout HEAD^ &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified_inside actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -639,12 +645,12 @@ test_expect_success 'rm of a populated nested submodule with nested modification
        git submodule update --recursive &&
        echo X >submod/subsubmod/empty &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified_inside actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -654,12 +660,12 @@ test_expect_success 'rm of a populated nested submodule with nested untracked fi
        git submodule update --recursive &&
        echo X >submod/subsubmod/untracked &&
        test_must_fail git rm submod &&
-       test -d submod &&
-       test -f submod/.git &&
+       test_path_is_dir submod &&
+       test_path_is_file submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect.modified_untracked actual &&
        git rm -f submod &&
-       test ! -d submod &&
+       test_path_is_missing submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
@@ -667,16 +673,17 @@ test_expect_success 'rm of a populated nested submodule with nested untracked fi
 test_expect_success "rm absorbs submodule's nested .git directory" '
        git reset --hard &&
        git submodule update --recursive &&
-       (cd submod/subsubmod &&
+       (
+               cd submod/subsubmod &&
                rm .git &&
                mv ../../.git/modules/sub/modules/sub .git &&
                GIT_WORK_TREE=. git config --unset core.worktree
        ) &&
        git rm submod 2>output.err &&
-       ! test -d submod &&
-       ! test -d submod/subsubmod/.git &&
+       test_path_is_missing submod &&
+       test_path_is_missing submod/subsubmod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test -s actual &&
+       test_file_not_empty actual &&
        test_i18ngrep Migrating output.err
 '
 
index 5f8272b6f94b513b250575c6e99e4f76d6b08e75..ea30d5f6a0f228971d29e257a89fca6ea594cecb 100755 (executable)
@@ -8,22 +8,22 @@ test_description='Test git stash'
 . ./test-lib.sh
 
 test_expect_success 'stash some dirty working directory' '
-       echo 1 > file &&
+       echo 1 >file &&
        git add file &&
        echo unrelated >other-file &&
        git add other-file &&
        test_tick &&
        git commit -m initial &&
-       echo 2 > file &&
+       echo 2 >file &&
        git add file &&
-       echo 3 > file &&
+       echo 3 >file &&
        test_tick &&
        git stash &&
        git diff-files --quiet &&
        git diff-index --cached --quiet HEAD
 '
 
-cat > expect << EOF
+cat >expect <<EOF
 diff --git a/file b/file
 index 0cfbf08..00750ed 100644
 --- a/file
@@ -35,7 +35,7 @@ EOF
 
 test_expect_success 'parents of stash' '
        test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
-       git diff stash^2..stash > output &&
+       git diff stash^2..stash >output &&
        test_cmp expect output
 '
 
@@ -74,7 +74,7 @@ test_expect_success 'apply stashed changes' '
 
 test_expect_success 'apply stashed changes (including index)' '
        git reset --hard HEAD^ &&
-       echo 6 > other-file &&
+       echo 6 >other-file &&
        git add other-file &&
        test_tick &&
        git commit -m other-file &&
@@ -99,12 +99,12 @@ test_expect_success 'stash drop complains of extra options' '
 
 test_expect_success 'drop top stash' '
        git reset --hard &&
-       git stash list > stashlist1 &&
-       echo 7 > file &&
+       git stash list >expected &&
+       echo 7 >file &&
        git stash &&
        git stash drop &&
-       git stash list > stashlist2 &&
-       test_cmp stashlist1 stashlist2 &&
+       git stash list >actual &&
+       test_cmp expected actual &&
        git stash apply &&
        test 3 = $(cat file) &&
        test 1 = $(git show :file) &&
@@ -113,9 +113,9 @@ test_expect_success 'drop top stash' '
 
 test_expect_success 'drop middle stash' '
        git reset --hard &&
-       echo 8 > file &&
+       echo 8 >file &&
        git stash &&
-       echo 9 > file &&
+       echo 9 >file &&
        git stash &&
        git stash drop stash@{1} &&
        test 2 = $(git stash list | wc -l) &&
@@ -160,7 +160,7 @@ test_expect_success 'stash pop' '
        test 0 = $(git stash list | wc -l)
 '
 
-cat > expect << EOF
+cat >expect <<EOF
 diff --git a/file2 b/file2
 new file mode 100644
 index 0000000..1fe912c
@@ -170,7 +170,7 @@ index 0000000..1fe912c
 +bar2
 EOF
 
-cat > expect1 << EOF
+cat >expect1 <<EOF
 diff --git a/file b/file
 index 257cc56..5716ca5 100644
 --- a/file
@@ -180,7 +180,7 @@ index 257cc56..5716ca5 100644
 +bar
 EOF
 
-cat > expect2 << EOF
+cat >expect2 <<EOF
 diff --git a/file b/file
 index 7601807..5716ca5 100644
 --- a/file
@@ -198,79 +198,79 @@ index 0000000..1fe912c
 EOF
 
 test_expect_success 'stash branch' '
-       echo foo > file &&
+       echo foo >file &&
        git commit file -m first &&
-       echo bar > file &&
-       echo bar2 > file2 &&
+       echo bar >file &&
+       echo bar2 >file2 &&
        git add file2 &&
        git stash &&
-       echo baz > file &&
+       echo baz >file &&
        git commit file -m second &&
        git stash branch stashbranch &&
        test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
        test $(git rev-parse HEAD) = $(git rev-parse master^) &&
-       git diff --cached > output &&
+       git diff --cached >output &&
        test_cmp expect output &&
-       git diff > output &&
+       git diff >output &&
        test_cmp expect1 output &&
        git add file &&
        git commit -m alternate\ second &&
-       git diff master..stashbranch > output &&
+       git diff master..stashbranch >output &&
        test_cmp output expect2 &&
        test 0 = $(git stash list | wc -l)
 '
 
 test_expect_success 'apply -q is quiet' '
-       echo foo > file &&
+       echo foo >file &&
        git stash &&
-       git stash apply -q > output.out 2>&1 &&
+       git stash apply -q >output.out 2>&1 &&
        test_must_be_empty output.out
 '
 
 test_expect_success 'save -q is quiet' '
-       git stash save --quiet > output.out 2>&1 &&
+       git stash save --quiet >output.out 2>&1 &&
        test_must_be_empty output.out
 '
 
 test_expect_success 'pop -q is quiet' '
-       git stash pop -q > output.out 2>&1 &&
+       git stash pop -q >output.out 2>&1 &&
        test_must_be_empty output.out
 '
 
 test_expect_success 'pop -q --index works and is quiet' '
-       echo foo > file &&
+       echo foo >file &&
        git add file &&
        git stash save --quiet &&
-       git stash pop -q --index > output.out 2>&1 &&
+       git stash pop -q --index >output.out 2>&1 &&
        test foo = "$(git show :file)" &&
        test_must_be_empty output.out
 '
 
 test_expect_success 'drop -q is quiet' '
        git stash &&
-       git stash drop -q > output.out 2>&1 &&
+       git stash drop -q >output.out 2>&1 &&
        test_must_be_empty output.out
 '
 
 test_expect_success 'stash -k' '
-       echo bar3 > file &&
-       echo bar4 > file2 &&
+       echo bar3 >file &&
+       echo bar4 >file2 &&
        git add file2 &&
        git stash -k &&
        test bar,bar4 = $(cat file),$(cat file2)
 '
 
 test_expect_success 'stash --no-keep-index' '
-       echo bar33 > file &&
-       echo bar44 > file2 &&
+       echo bar33 >file &&
+       echo bar44 >file2 &&
        git add file2 &&
        git stash --no-keep-index &&
        test bar,bar2 = $(cat file),$(cat file2)
 '
 
 test_expect_success 'stash --invalid-option' '
-       echo bar5 > file &&
-       echo bar6 > file2 &&
+       echo bar5 >file &&
+       echo bar6 >file2 &&
        git add file2 &&
        test_must_fail git stash --invalid-option &&
        test_must_fail git stash save --invalid-option &&
@@ -287,6 +287,14 @@ test_expect_success 'stash an added file' '
        test new = "$(cat file3)"
 '
 
+test_expect_success 'stash --intent-to-add file' '
+       git reset --hard &&
+       echo new >file4 &&
+       git add --intent-to-add file4 &&
+       test_when_finished "git rm -f file4" &&
+       test_must_fail git stash
+'
+
 test_expect_success 'stash rm then recreate' '
        git reset --hard &&
        git rm file &&
@@ -444,6 +452,36 @@ test_expect_failure 'stash file to directory' '
        test foo = "$(cat file/file)"
 '
 
+test_expect_success 'giving too many ref arguments does not modify files' '
+       git stash clear &&
+       test_when_finished "git reset --hard HEAD" &&
+       echo foo >file2 &&
+       git stash &&
+       echo bar >file2 &&
+       git stash &&
+       test-tool chmtime =123456789 file2 &&
+       for type in apply pop "branch stash-branch"
+       do
+               test_must_fail git stash $type stash@{0} stash@{1} 2>err &&
+               test_i18ngrep "Too many revisions" err &&
+               test 123456789 = $(test-tool chmtime -g file2) || return 1
+       done
+'
+
+test_expect_success 'drop: too many arguments errors out (does nothing)' '
+       git stash list >expect &&
+       test_must_fail git stash drop stash@{0} stash@{1} 2>err &&
+       test_i18ngrep "Too many revisions" err &&
+       git stash list >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'show: too many arguments errors out (does nothing)' '
+       test_must_fail git stash show stash@{0} stash@{1} 2>err 1>out &&
+       test_i18ngrep "Too many revisions" err &&
+       test_must_be_empty out
+'
+
 test_expect_success 'stash create - no changes' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
@@ -456,11 +494,12 @@ test_expect_success 'stash branch - no stashes on stack, stash-like argument' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
-       echo foo >> file &&
+       echo foo >>file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        git stash branch stash-branch ${STASH_ID} &&
-       test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+       test_when_finished "git reset --hard HEAD && git checkout master &&
+       git branch -D stash-branch" &&
        test $(git ls-files --modified | wc -l) -eq 1
 '
 
@@ -468,25 +507,31 @@ test_expect_success 'stash branch - stashes on stack, stash-like argument' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
-       echo foo >> file &&
+       echo foo >>file &&
        git stash &&
        test_when_finished "git stash drop" &&
-       echo bar >> file &&
+       echo bar >>file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        git stash branch stash-branch ${STASH_ID} &&
-       test_when_finished "git reset --hard HEAD && git checkout master && git branch -D stash-branch" &&
+       test_when_finished "git reset --hard HEAD && git checkout master &&
+       git branch -D stash-branch" &&
        test $(git ls-files --modified | wc -l) -eq 1
 '
 
+test_expect_success 'stash branch complains with no arguments' '
+       test_must_fail git stash branch 2>err &&
+       test_i18ngrep "No branch name specified" err
+'
+
 test_expect_success 'stash show format defaults to --stat' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
-       echo foo >> file &&
+       echo foo >>file &&
        git stash &&
        test_when_finished "git stash drop" &&
-       echo bar >> file &&
+       echo bar >>file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        cat >expected <<-EOF &&
@@ -501,10 +546,10 @@ test_expect_success 'stash show - stashes on stack, stash-like argument' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
-       echo foo >> file &&
+       echo foo >>file &&
        git stash &&
        test_when_finished "git stash drop" &&
-       echo bar >> file &&
+       echo bar >>file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        echo "1 0       file" >expected &&
@@ -516,10 +561,10 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
-       echo foo >> file &&
+       echo foo >>file &&
        git stash &&
        test_when_finished "git stash drop" &&
-       echo bar >> file &&
+       echo bar >>file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        cat >expected <<-EOF &&
@@ -539,7 +584,7 @@ test_expect_success 'stash show - no stashes on stack, stash-like argument' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
-       echo foo >> file &&
+       echo foo >>file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        echo "1 0       file" >expected &&
@@ -551,7 +596,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD" &&
        git reset --hard &&
-       echo foo >> file &&
+       echo foo >>file &&
        STASH_ID=$(git stash create) &&
        git reset --hard &&
        cat >expected <<-EOF &&
@@ -567,13 +612,31 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
        test_cmp expected actual
 '
 
-test_expect_success 'stash drop - fail early if specified stash is not a stash reference' '
+test_expect_success 'stash show --patience shows diff' '
+       git reset --hard &&
+       echo foo >>file &&
+       STASH_ID=$(git stash create) &&
+       git reset --hard &&
+       cat >expected <<-EOF &&
+       diff --git a/file b/file
+       index 7601807..71b52c4 100644
+       --- a/file
+       +++ b/file
+       @@ -1 +1,2 @@
+        baz
+       +foo
+       EOF
+       git stash show --patience ${STASH_ID} >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'drop: fail early if specified stash is not a stash ref' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD && git stash clear" &&
        git reset --hard &&
-       echo foo > file &&
+       echo foo >file &&
        git stash &&
-       echo bar > file &&
+       echo bar >file &&
        git stash &&
        test_must_fail git stash drop $(git rev-parse stash@{0}) &&
        git stash pop &&
@@ -581,13 +644,13 @@ test_expect_success 'stash drop - fail early if specified stash is not a stash r
        git reset --hard HEAD
 '
 
-test_expect_success 'stash pop - fail early if specified stash is not a stash reference' '
+test_expect_success 'pop: fail early if specified stash is not a stash ref' '
        git stash clear &&
        test_when_finished "git reset --hard HEAD && git stash clear" &&
        git reset --hard &&
-       echo foo > file &&
+       echo foo >file &&
        git stash &&
-       echo bar > file &&
+       echo bar >file &&
        git stash &&
        test_must_fail git stash pop $(git rev-parse stash@{0}) &&
        git stash pop &&
@@ -597,8 +660,8 @@ test_expect_success 'stash pop - fail early if specified stash is not a stash re
 
 test_expect_success 'ref with non-existent reflog' '
        git stash clear &&
-       echo bar5 > file &&
-       echo bar6 > file2 &&
+       echo bar5 >file &&
+       echo bar6 >file2 &&
        git add file2 &&
        git stash &&
        test_must_fail git rev-parse --quiet --verify does-not-exist &&
@@ -618,8 +681,8 @@ test_expect_success 'ref with non-existent reflog' '
 test_expect_success 'invalid ref of the form stash@{n}, n >= N' '
        git stash clear &&
        test_must_fail git stash drop stash@{0} &&
-       echo bar5 > file &&
-       echo bar6 > file2 &&
+       echo bar5 >file &&
+       echo bar6 >file2 &&
        git add file2 &&
        git stash &&
        test_must_fail git stash drop stash@{1} &&
@@ -645,7 +708,7 @@ test_expect_success 'invalid ref of the form "n", n >= N' '
        git stash drop
 '
 
-test_expect_success 'stash branch should not drop the stash if the branch exists' '
+test_expect_success 'branch: do not drop the stash if the branch exists' '
        git stash clear &&
        echo foo >file &&
        git add file &&
@@ -656,7 +719,7 @@ test_expect_success 'stash branch should not drop the stash if the branch exists
        git rev-parse stash@{0} --
 '
 
-test_expect_success 'stash branch should not drop the stash if the apply fails' '
+test_expect_success 'branch: should not drop the stash if the apply fails' '
        git stash clear &&
        git reset HEAD~1 --hard &&
        echo foo >file &&
@@ -670,7 +733,7 @@ test_expect_success 'stash branch should not drop the stash if the apply fails'
        git rev-parse stash@{0} --
 '
 
-test_expect_success 'stash apply shows status same as git status (relative to current directory)' '
+test_expect_success 'apply: show same status as git status (relative to ./)' '
        git stash clear &&
        echo 1 >subdir/subfile1 &&
        echo 2 >subdir/subfile2 &&
@@ -689,7 +752,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
        test_i18ncmp expect actual
 '
 
-cat > expect << EOF
+cat >expect <<EOF
 diff --git a/HEAD b/HEAD
 new file mode 100644
 index 0000000..fe0cbee
@@ -702,14 +765,14 @@ EOF
 test_expect_success 'stash where working directory contains "HEAD" file' '
        git stash clear &&
        git reset --hard &&
-       echo file-not-a-ref > HEAD &&
+       echo file-not-a-ref >HEAD &&
        git add HEAD &&
        test_tick &&
        git stash &&
        git diff-files --quiet &&
        git diff-index --cached --quiet HEAD &&
        test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
-       git diff stash^..stash > output &&
+       git diff stash^..stash >output &&
        test_cmp expect output
 '
 
@@ -1011,7 +1074,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' '
        test_i18ncmp expect actual
 '
 
-test_expect_success 'stash push with pathspec shows no changes when there are none' '
+test_expect_success 'push <pathspec>: show no changes when there are none' '
        >foo &&
        git add foo &&
        git commit -m "tmp" &&
@@ -1021,12 +1084,35 @@ test_expect_success 'stash push with pathspec shows no changes when there are no
        test_i18ncmp expect actual
 '
 
-test_expect_success 'stash push with pathspec not in the repository errors out' '
+test_expect_success 'push: <pathspec> not in the repository errors out' '
        >untracked &&
        test_must_fail git stash push untracked &&
        test_path_is_file untracked
 '
 
+test_expect_success 'push: -q is quiet with changes' '
+       >foo &&
+       git add foo &&
+       git stash push -q >output 2>&1 &&
+       test_must_be_empty output
+'
+
+test_expect_success 'push: -q is quiet with no changes' '
+       git stash push -q >output 2>&1 &&
+       test_must_be_empty output
+'
+
+test_expect_success 'push: -q is quiet even if there is no initial commit' '
+       git init foo_dir &&
+       test_when_finished rm -rf foo_dir &&
+       (
+               cd foo_dir &&
+               >bar &&
+               test_must_fail git stash push -q >output 2>&1 &&
+               test_must_be_empty output
+       )
+'
+
 test_expect_success 'untracked files are left in place when -u is not given' '
        >file &&
        git add file &&
@@ -1096,6 +1182,12 @@ test_expect_success 'stash -- <subdir> works with binary files' '
        test_path_is_file subdir/untracked
 '
 
+test_expect_success 'stash with user.name and user.email set works' '
+       test_config user.name "A U Thor" &&
+       test_config user.email "a.u@thor" &&
+       git stash
+'
+
 test_expect_success 'stash works when user.name and user.email are not set' '
        git reset &&
        >1 &&
index cc1c8a7bb2da508e570621ddf9e08694007425d4..29ca76f2fbea9275462339ce2f308b6d6920aac5 100755 (executable)
@@ -283,4 +283,10 @@ test_expect_success 'stash -u -- <non-existant> shows no changes when there are
        test_i18ncmp expect actual
 '
 
+test_expect_success 'stash -u with globs' '
+       >untracked.txt &&
+       git stash -u -- ":(glob)**/*.txt" &&
+       test_path_is_missing untracked.txt
+'
+
 test_done
diff --git a/t/t3907-stash-show-config.sh b/t/t3907-stash-show-config.sh
new file mode 100755 (executable)
index 0000000..10914bb
--- /dev/null
@@ -0,0 +1,83 @@
+#!/bin/sh
+
+test_description='Test git stash show configuration.'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       test_commit file
+'
+
+# takes three parameters:
+# 1. the stash.showStat value (or "<unset>")
+# 2. the stash.showPatch value (or "<unset>")
+# 3. the diff options of the expected output (or nothing for no output)
+test_stat_and_patch () {
+       if test "<unset>" = "$1"
+       then
+               test_unconfig stash.showStat
+       else
+               test_config stash.showStat "$1"
+       fi &&
+
+       if test "<unset>" = "$2"
+       then
+               test_unconfig stash.showPatch
+       else
+               test_config stash.showPatch "$2"
+       fi &&
+
+       shift 2 &&
+       echo 2 >file.t &&
+       if test $# != 0
+       then
+               git diff "$@" >expect
+       fi &&
+       git stash &&
+       git stash show >actual &&
+
+       if test $# = 0
+       then
+               test_must_be_empty actual
+       else
+               test_cmp expect actual
+       fi
+}
+
+test_expect_success 'showStat unset showPatch unset' '
+       test_stat_and_patch "<unset>" "<unset>" --stat
+'
+
+test_expect_success 'showStat unset showPatch false' '
+       test_stat_and_patch "<unset>" false --stat
+'
+
+test_expect_success 'showStat unset showPatch true' '
+       test_stat_and_patch "<unset>" true --stat -p
+'
+
+test_expect_success 'showStat false showPatch unset' '
+       test_stat_and_patch false "<unset>"
+'
+
+test_expect_success 'showStat false showPatch false' '
+       test_stat_and_patch false false
+'
+
+test_expect_success 'showStat false showPatch true' '
+       test_stat_and_patch false true -p
+'
+
+test_expect_success 'showStat true showPatch unset' '
+       test_stat_and_patch true "<unset>" --stat
+'
+
+test_expect_success 'showStat true showPatch false' '
+       test_stat_and_patch true false --stat
+'
+
+test_expect_success 'showStat true showPatch true' '
+       test_stat_and_patch true true --stat -p
+'
+
+test_done
index 909c743c134c5e18e0a80db3eae2dfb6e8210e6a..b6e2fdbc4410f1b8b04586f12bb8806e85a10e8d 100755 (executable)
@@ -589,6 +589,12 @@ test_expect_success 'excessive subject' '
        ls patches/0004-This-is-an-excessively-long-subject-line-for-a-messa.patch
 '
 
+test_expect_success 'failure to write cover-letter aborts gracefully' '
+       test_when_finished "rmdir 0000-cover-letter.patch" &&
+       mkdir 0000-cover-letter.patch &&
+       test_must_fail git format-patch --no-renames --cover-letter -1
+'
+
 test_expect_success 'cover-letter inherits diff options' '
        git mv file foo &&
        git commit -m foo &&
index 07b49f6d6d9ef93476af2af10fe694d2f971b59d..d4afe125548bed5e7e881d2bb4ae54f237968f28 100755 (executable)
@@ -480,18 +480,18 @@ test_expect_success FUNNYNAMES 'setup for --combined-all-paths with funny names'
        git branch side1d &&
        git branch side2d &&
        git checkout side1d &&
-       test_seq 1 10 >$(printf "file\twith\ttabs") &&
+       test_seq 1 10 >"$(printf "file\twith\ttabs")" &&
        git add file* &&
        git commit -m with &&
        git checkout side2d &&
-       test_seq 1 9 >$(printf "i\tam\ttabbed") &&
-       echo ten >>$(printf "i\tam\ttabbed") &&
+       test_seq 1 9 >"$(printf "i\tam\ttabbed")" &&
+       echo ten >>"$(printf "i\tam\ttabbed")" &&
        git add *tabbed &&
        git commit -m iam &&
        git checkout -b funny-names-mergery side1d &&
        git merge --no-commit side2d &&
        git rm *tabs &&
-       echo eleven >>$(printf "i\tam\ttabbed") &&
+       echo eleven >>"$(printf "i\tam\ttabbed")" &&
        git mv "$(printf "i\tam\ttabbed")" "$(printf "fickle\tnaming")" &&
        git add fickle* &&
        git commit
index 55b577d919b5d82dd8c8bc81a6a4bc657b190f41..3f7f750cc8d7442458323f9ec77a371db3818320 100755 (executable)
@@ -77,14 +77,12 @@ test_expect_success 'setup: messages' '
 
        printf "Subject: " >subject-prefix &&
 
-       cat - subject-prefix msg-without-scissors-line >msg-with-scissors-line <<-\EOF &&
+       cat - subject-prefix msg-without-scissors-line >msg-with-scissors-line <<-\EOF
        This line should not be included in the commit message with --scissors enabled.
 
         - - >8 - - remove everything above this line - - >8 - -
 
        EOF
-
-       signoff="Signed-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>"
 '
 
 test_expect_success setup '
index bd5fe4d1483005175d9c2211dee1901c2b132573..1db7bd0f59b46768d6559c37e026cde41821763a 100755 (executable)
@@ -115,4 +115,21 @@ test_expect_success 'range_set_union' '
        git log $(for x in $(test_seq 200); do echo -L $((2*x)),+1:c.c; done)
 '
 
+test_expect_success '-s shows only line-log commits' '
+       git log --format="commit %s" -L1,24:b.c >expect.raw &&
+       grep ^commit expect.raw >expect &&
+       git log --format="commit %s" -L1,24:b.c -s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '-p shows the default patch output' '
+       git log -L1,24:b.c >expect &&
+       git log -L1,24:b.c -p >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--raw is forbidden' '
+       test_must_fail git log -L1,24:b.c --raw
+'
+
 test_done
index 24541ea137517354a8b738bdef34728b49f2850e..4c0201c34be59715f352ac2e5d1a9422311eedb5 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success 'verify blob count in normal packfile' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r1 pack-objects --rev --stdout >all.pack <<-EOF &&
+       git -C r1 pack-objects --revs --stdout >all.pack <<-EOF &&
        HEAD
        EOF
        git -C r1 index-pack ../all.pack &&
@@ -39,7 +39,7 @@ test_expect_success 'verify blob count in normal packfile' '
 '
 
 test_expect_success 'verify blob:none packfile has no blobs' '
-       git -C r1 pack-objects --rev --stdout --filter=blob:none >filter.pack <<-EOF &&
+       git -C r1 pack-objects --revs --stdout --filter=blob:none >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r1 index-pack ../filter.pack &&
@@ -74,7 +74,7 @@ test_expect_success 'get an error for missing tree object' '
        git -C r5 commit -m "foo" &&
        del=$(git -C r5 rev-parse HEAD^{tree} | sed "s|..|&/|") &&
        rm r5/.git/objects/$del &&
-       test_must_fail git -C r5 pack-objects --rev --stdout 2>bad_tree <<-EOF &&
+       test_must_fail git -C r5 pack-objects --revs --stdout 2>bad_tree <<-EOF &&
        HEAD
        EOF
        grep "bad tree object" bad_tree
@@ -88,7 +88,7 @@ test_expect_success 'setup for tests of tree:0' '
 '
 
 test_expect_success 'verify tree:0 packfile has no blobs or trees' '
-       git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+       git -C r1 pack-objects --revs --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
        HEAD
        EOF
        git -C r1 index-pack ../commitsonly.pack &&
@@ -98,7 +98,7 @@ test_expect_success 'verify tree:0 packfile has no blobs or trees' '
 
 test_expect_success 'grab tree directly when using tree:0' '
        # We should get the tree specified directly but not its blobs or subtrees.
-       git -C r1 pack-objects --rev --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
+       git -C r1 pack-objects --revs --stdout --filter=tree:0 >commitsonly.pack <<-EOF &&
        HEAD:
        EOF
        git -C r1 index-pack ../commitsonly.pack &&
@@ -128,7 +128,7 @@ test_expect_success 'verify blob count in normal packfile' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r2 pack-objects --rev --stdout >all.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout >all.pack <<-EOF &&
        HEAD
        EOF
        git -C r2 index-pack ../all.pack &&
@@ -142,7 +142,7 @@ test_expect_success 'verify blob count in normal packfile' '
 '
 
 test_expect_success 'verify blob:limit=500 omits all blobs' '
-       git -C r2 pack-objects --rev --stdout --filter=blob:limit=500 >filter.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=500 >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r2 index-pack ../filter.pack &&
@@ -157,7 +157,7 @@ test_expect_success 'verify blob:limit=500 omits all blobs' '
 '
 
 test_expect_success 'verify blob:limit=1000' '
-       git -C r2 pack-objects --rev --stdout --filter=blob:limit=1000 >filter.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=1000 >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r2 index-pack ../filter.pack &&
@@ -176,7 +176,7 @@ test_expect_success 'verify blob:limit=1001' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r2 pack-objects --rev --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r2 index-pack ../filter.pack &&
@@ -194,7 +194,7 @@ test_expect_success 'verify blob:limit=10001' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r2 pack-objects --rev --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r2 index-pack ../filter.pack &&
@@ -212,7 +212,7 @@ test_expect_success 'verify blob:limit=1k' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r2 index-pack ../filter.pack &&
@@ -230,7 +230,7 @@ test_expect_success 'verify explicitly specifying oversized blob in input' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
        HEAD
        $(git -C r2 rev-parse HEAD:large.10000)
        EOF
@@ -249,7 +249,7 @@ test_expect_success 'verify blob:limit=1m' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r2 pack-objects --rev --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
+       git -C r2 pack-objects --revs --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r2 index-pack ../filter.pack &&
@@ -302,7 +302,7 @@ test_expect_success 'verify blob count in normal packfile' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r3 pack-objects --rev --stdout >all.pack <<-EOF &&
+       git -C r3 pack-objects --revs --stdout >all.pack <<-EOF &&
        HEAD
        EOF
        git -C r3 index-pack ../all.pack &&
@@ -320,7 +320,7 @@ test_expect_success 'verify sparse:path=pattern1' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern1 >filter.pack <<-EOF &&
+       git -C r3 pack-objects --revs --stdout --filter=sparse:path=../pattern1 >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r3 index-pack ../filter.pack &&
@@ -352,7 +352,7 @@ test_expect_success 'verify sparse:path=pattern2' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern2 >filter.pack <<-EOF &&
+       git -C r3 pack-objects --revs --stdout --filter=sparse:path=../pattern2 >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r3 index-pack ../filter.pack &&
@@ -404,7 +404,7 @@ test_expect_success 'verify blob count in normal packfile' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r4 pack-objects --rev --stdout >all.pack <<-EOF &&
+       git -C r4 pack-objects --revs --stdout >all.pack <<-EOF &&
        HEAD
        EOF
        git -C r4 index-pack ../all.pack &&
@@ -423,7 +423,7 @@ test_expect_success 'verify sparse:oid=OID' '
        sort >expected &&
 
        oid=$(git -C r4 ls-files -s pattern | awk -f print_2.awk) &&
-       git -C r4 pack-objects --rev --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
+       git -C r4 pack-objects --revs --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r4 index-pack ../filter.pack &&
@@ -441,7 +441,7 @@ test_expect_success 'verify sparse:oid=oid-ish' '
        awk -f print_2.awk ls_files_result |
        sort >expected &&
 
-       git -C r4 pack-objects --rev --stdout --filter=sparse:oid=master:pattern >filter.pack <<-EOF &&
+       git -C r4 pack-objects --revs --stdout --filter=sparse:oid=master:pattern >filter.pack <<-EOF &&
        HEAD
        EOF
        git -C r4 index-pack ../filter.pack &&
@@ -470,19 +470,19 @@ test_expect_success 'setup r1 - delete loose blobs' '
 '
 
 test_expect_success 'verify pack-objects fails w/ missing objects' '
-       test_must_fail git -C r1 pack-objects --rev --stdout >miss.pack <<-EOF
+       test_must_fail git -C r1 pack-objects --revs --stdout >miss.pack <<-EOF
        HEAD
        EOF
 '
 
 test_expect_success 'verify pack-objects fails w/ --missing=error' '
-       test_must_fail git -C r1 pack-objects --rev --stdout --missing=error >miss.pack <<-EOF
+       test_must_fail git -C r1 pack-objects --revs --stdout --missing=error >miss.pack <<-EOF
        HEAD
        EOF
 '
 
 test_expect_success 'verify pack-objects w/ --missing=allow-any' '
-       git -C r1 pack-objects --rev --stdout --missing=allow-any >miss.pack <<-EOF
+       git -C r1 pack-objects --revs --stdout --missing=allow-any >miss.pack <<-EOF
        HEAD
        EOF
 '
index 561796f2809c38d3b5229abd4359f5b72141839e..069e4e28e42d69e7b892112f3ba3743e4e13edac 100755 (executable)
@@ -75,7 +75,7 @@ graph_read_expect() {
 
 test_expect_success 'write graph' '
        cd "$TRASH_DIRECTORY/full" &&
-       graph1=$(git commit-graph write) &&
+       git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
        graph_read_expect "3"
 '
index f1932ea431dca6d5aa2ad71575b7767413eeac18..571d620aedbac122960b319130ebad64a49d4f5b 100755 (executable)
@@ -288,7 +288,7 @@ test_expect_success 'receive-pack de-dupes .have lines' '
        $shared .have
        EOF
 
-       GIT_TRACE_PACKET=$(pwd)/trace \
+       GIT_TRACE_PACKET=$(pwd)/trace GIT_TEST_PROTOCOL_VERSION= \
            git push \
                --receive-pack="unset GIT_TRACE_PACKET; git-receive-pack" \
                fork HEAD:foo &&
index a4a5903cbaee5c2a67963d8746d0252ba59bf163..7344253bfbbcb3f36533db2657f974de97951fde 100755 (executable)
@@ -131,7 +131,7 @@ test_expect_success 'git rebase -m --skip' '
 test_expect_success 'git rebase with implicit use of interactive backend' '
        git reset --hard D &&
        clear_hook_input &&
-       test_must_fail git rebase --keep --onto A B &&
+       test_must_fail git rebase --keep-empty --onto A B &&
        echo C > foo &&
        git add foo &&
        git rebase --continue &&
@@ -146,7 +146,7 @@ test_expect_success 'git rebase with implicit use of interactive backend' '
 test_expect_success 'git rebase --skip with implicit use of interactive backend' '
        git reset --hard D &&
        clear_hook_input &&
-       test_must_fail git rebase --keep --onto A B &&
+       test_must_fail git rebase --keep-empty --onto A B &&
        test_must_fail git rebase --skip &&
        echo D > foo &&
        git add foo &&
index 49c540b1e1dbd5fc7d9a7baa36ee93a2ed3fc3c3..1c71c0ec770cd54505f65bf83eca91f153cdf785 100755 (executable)
@@ -636,7 +636,9 @@ test_expect_success 'fetch-pack cannot fetch a raw sha1 that is not advertised a
        test_commit -C server 6 &&
 
        git init client &&
-       test_must_fail git -C client fetch-pack ../server \
+       # Some protocol versions (e.g. 2) support fetching
+       # unadvertised objects, so restrict this test to v0.
+       test_must_fail env GIT_TEST_PROTOCOL_VERSION= git -C client fetch-pack ../server \
                $(git -C server rev-parse refs/heads/master^) 2>err &&
        test_i18ngrep "Server does not allow request for unadvertised object" err
 '
@@ -918,7 +920,4 @@ test_expect_success 'fetch with --filter=blob:limit=0 and HTTP' '
        fetch_filter_blob_limit_zero "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
 '
 
-stop_httpd
-
-
 test_done
index 4ca48f02761d4379bf5e01cb862e47830ec0c5ac..6041a4dd3278c7701f5e9dd46bfdab7ffbd0cc7a 100755 (executable)
@@ -47,7 +47,7 @@ get_needs () {
        test -s "$1" &&
        perl -alne '
                next unless $F[1] eq "upload-pack<";
-               last if $F[2] eq "0000";
+               next unless $F[2] eq "want";
                print $F[2], " ", $F[3];
        ' "$1"
 }
index 3b7b30568cddd5d9ea1a8979e5366a262f81bf09..e98d90dd9baf644bf2013aea51a91eab4e9ef73c 100755 (executable)
@@ -978,6 +978,4 @@ test_expect_success '--negotiation-tip limits "have" lines sent with HTTP protoc
        check_negotiation_tip
 '
 
-stop_httpd
-
 test_done
index ced15ae1224247355125fcbad7ad2184f9861cbe..e3c4a48c8536e46e0aa36b4d862b3876617335a4 100755 (executable)
@@ -223,7 +223,9 @@ test_expect_success 'ls-remote --symref' '
        $(git rev-parse refs/tags/mark1.10)     refs/tags/mark1.10
        $(git rev-parse refs/tags/mark1.2)      refs/tags/mark1.2
        EOF
-       git ls-remote --symref >actual &&
+       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+       # protocol v0 here.
+       GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref >actual &&
        test_cmp expect actual
 '
 
@@ -232,7 +234,9 @@ test_expect_success 'ls-remote with filtered symref (refname)' '
        ref: refs/heads/master  HEAD
        1bd44cb9d13204b0fe1958db0082f5028a16eb3a        HEAD
        EOF
-       git ls-remote --symref . HEAD >actual &&
+       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+       # protocol v0 here.
+       GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref . HEAD >actual &&
        test_cmp expect actual
 '
 
@@ -243,7 +247,9 @@ test_expect_failure 'ls-remote with filtered symref (--heads)' '
        1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/foo
        1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/master
        EOF
-       git ls-remote --symref --heads . >actual &&
+       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+       # protocol v0 here.
+       GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref --heads . >actual &&
        test_cmp expect actual
 '
 
@@ -252,9 +258,11 @@ test_expect_success 'ls-remote --symref omits filtered-out matches' '
        1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/foo
        1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/master
        EOF
-       git ls-remote --symref --heads . >actual &&
+       # Protocol v2 supports sending symrefs for refs other than HEAD, so use
+       # protocol v0 here.
+       GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref --heads . >actual &&
        test_cmp expect actual &&
-       git ls-remote --symref . "refs/heads/*" >actual &&
+       GIT_TEST_PROTOCOL_VERSION= git ls-remote --symref . "refs/heads/*" >actual &&
        test_cmp expect actual
 '
 
index 36b0dbc01cffc06aefb180179965ccbaa9c983d8..e55d8474efb6891d4bea9edf5488f793b26e135a 100755 (executable)
@@ -6,6 +6,10 @@
 
 test_description='Merge logic in fetch'
 
+# NEEDSWORK: If the overspecification of the expected result is reduced, we
+# might be able to run this test in all protocol versions.
+GIT_TEST_PROTOCOL_VERSION=
+
 . ./test-lib.sh
 
 LF='
index 37e8e80893dad130c5c00882796047417aae115b..49bf4280e85cae7da71b54192ab83879931283bd 100755 (executable)
@@ -1147,8 +1147,12 @@ test_expect_success 'fetch exact SHA1' '
                git prune &&
                test_must_fail git cat-file -t $the_commit &&
 
+               # Some protocol versions (e.g. 2) support fetching
+               # unadvertised objects, so restrict this test to v0.
+
                # fetching the hidden object should fail by default
-               test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
+               test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+                       git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
                test_i18ngrep "Server does not allow request for unadvertised object" err &&
                test_must_fail git rev-parse --verify refs/heads/copy &&
 
@@ -1204,7 +1208,10 @@ do
                mk_empty shallow &&
                (
                        cd shallow &&
-                       test_must_fail git fetch --depth=1 ../testrepo/.git $SHA1 &&
+                       # Some protocol versions (e.g. 2) support fetching
+                       # unadvertised objects, so restrict this test to v0.
+                       test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+                               git fetch --depth=1 ../testrepo/.git $SHA1 &&
                        git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
                        git fetch --depth=1 ../testrepo/.git $SHA1 &&
                        git cat-file commit $SHA1
@@ -1232,15 +1239,20 @@ do
                mk_empty shallow &&
                (
                        cd shallow &&
-                       test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3 &&
-                       test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_1 &&
+                       # Some protocol versions (e.g. 2) support fetching
+                       # unadvertised objects, so restrict this test to v0.
+                       test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+                               git fetch ../testrepo/.git $SHA1_3 &&
+                       test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+                               git fetch ../testrepo/.git $SHA1_1 &&
                        git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
                        git fetch ../testrepo/.git $SHA1_1 &&
                        git cat-file commit $SHA1_1 &&
                        test_must_fail git cat-file commit $SHA1_2 &&
                        git fetch ../testrepo/.git $SHA1_2 &&
                        git cat-file commit $SHA1_2 &&
-                       test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3
+                       test_must_fail ok=sigpipe env GIT_TEST_PROTOCOL_VERSION= \
+                               git fetch ../testrepo/.git $SHA1_3
                )
        '
 done
@@ -1370,7 +1382,7 @@ test_expect_success 'push does not follow tags by default' '
        test_cmp expect actual
 '
 
-test_expect_success 'push --follow-tag only pushes relevant tags' '
+test_expect_success 'push --follow-tags only pushes relevant tags' '
        mk_test testrepo heads/master &&
        rm -fr src dst &&
        git init src &&
@@ -1384,7 +1396,7 @@ test_expect_success 'push --follow-tag only pushes relevant tags' '
                git tag -m "future" future &&
                git checkout master &&
                git for-each-ref refs/heads/master refs/tags/tag >../expect &&
-               git push --follow-tag ../dst master
+               git push --follow-tags ../dst master
        ) &&
        (
                cd dst &&
index e2c37fd9785d2a47f85853a3611006609381c54c..4ad059e6bedf8ddf2a0db9c20f70ee1c3cd6da15 100755 (executable)
@@ -363,7 +363,7 @@ test_expect_success 'push succeeds if submodule has no remote and is on the firs
                ) &&
                git add b &&
                git commit -m "added submodule" &&
-               git push --recurse-submodule=check origin master
+               git push --recurse-submodules=check origin master
        )
 '
 
index 6caf628efaaa400473ddfb46fdef0b847b4edd39..66f0b64d39273d9e2f6ae5c74e195e50e4b712c5 100755 (executable)
@@ -255,6 +255,4 @@ test_expect_success 'shallow fetches check connectivity before writing shallow f
        git -C client fsck
 '
 
-stop_httpd
-
 test_done
index 5fbf67c4468897184f46ef9ea151f1dd6ad1d8d0..b4ad81f00635efc144a3b531a939e5fc34d5c14f 100755 (executable)
@@ -67,7 +67,10 @@ test_expect_success 'no shallow lines after receiving ACK ready' '
                cd clone &&
                git checkout --orphan newnew &&
                test_commit new-too &&
-               GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" git fetch --depth=2 &&
+               # NEEDSWORK: If the overspecification of the expected result is reduced, we
+               # might be able to run this test in all protocol versions.
+               GIT_TRACE_PACKET="$TRASH_DIRECTORY/trace" GIT_TEST_PROTOCOL_VERSION= \
+                       git fetch --depth=2 &&
                grep "fetch-pack< ACK .* ready" ../trace &&
                ! grep "fetch-pack> done" ../trace
        )
@@ -146,5 +149,4 @@ test_expect_success 'fetching deepen' '
        )
 '
 
-stop_httpd
 test_done
index 88ff5a49e4af38daca65c6e3a79ea4030b391c6b..a094fd5e71334bd16ac890234962485a7f9b7d24 100755 (executable)
@@ -176,6 +176,4 @@ test_expect_failure 'push to password-protected repository (no user in URL)' '
        test_cmp expect actual
 '
 
-stop_httpd
-
 test_done
index 5475afc052934677c4f87adf4f29da507b4c6844..8ef8763e063cf30da8af4a80079458cf8e18c56d 100755 (executable)
@@ -47,7 +47,12 @@ test_expect_success 'no empty path components' '
        cd "$ROOT_PATH" &&
        git clone $HTTPD_URL/smart/test_repo.git/ test_repo_clone &&
 
-       check_access_log exp
+       # NEEDSWORK: If the overspecification of the expected result is reduced, we
+       # might be able to run this test in all protocol versions.
+       if test -z "$GIT_TEST_PROTOCOL_VERSION"
+       then
+               check_access_log exp
+       fi
 '
 
 test_expect_success 'clone remote repository' '
@@ -128,7 +133,12 @@ GET  /smart/test_repo.git/info/refs?service=git-receive-pack HTTP/1.1 200
 POST /smart/test_repo.git/git-receive-pack HTTP/1.1 200
 EOF
 test_expect_success 'used receive-pack service' '
-       check_access_log exp
+       # NEEDSWORK: If the overspecification of the expected result is reduced, we
+       # might be able to run this test in all protocol versions.
+       if test -z "$GIT_TEST_PROTOCOL_VERSION"
+       then
+               check_access_log exp
+       fi
 '
 
 test_http_push_nonff "$HTTPD_DOCUMENT_ROOT_PATH"/test_repo.git \
@@ -373,5 +383,4 @@ test_expect_success 'colorize errors/hints' '
        test_i18ngrep ! "^hint: " decoded
 '
 
-stop_httpd
 test_done
index 51658331571611c568def54be28072f426c48e66..ddc1db722d43ce3cfbf80b46deea0a69a4973247 100755 (executable)
@@ -90,5 +90,4 @@ EOF
        )
 '
 
-stop_httpd
 test_done
index b47a95871cac3dd8593b3b9262d238be3914ac62..6d1d59c9b1af8c1ca7d6e78c8a589395ff971338 100755 (executable)
@@ -278,6 +278,4 @@ test_expect_success 'push options keep quoted characters intact (http)' '
        test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options
 '
 
-stop_httpd
-
 test_done
index 6d7d88ccc906a4c73285550ec80cd3fe67a764ee..b811d89cfd6df25fb2e42db6f05a69cf0b7a6c3b 100755 (executable)
@@ -408,5 +408,20 @@ test_expect_success 'print HTTP error when any intermediate redirect throws erro
        test_i18ngrep "unable to access.*/redir-to/502" stderr
 '
 
-stop_httpd
+test_expect_success 'fetching via http alternates works' '
+       parent=$HTTPD_DOCUMENT_ROOT_PATH/alt-parent.git &&
+       git init --bare "$parent" &&
+       git -C "$parent" --work-tree=. commit --allow-empty -m foo &&
+       git -C "$parent" update-server-info &&
+       commit=$(git -C "$parent" rev-parse HEAD) &&
+
+       child=$HTTPD_DOCUMENT_ROOT_PATH/alt-child.git &&
+       git init --bare "$child" &&
+       echo "../../alt-parent.git/objects" >"$child/objects/info/alternates" &&
+       git -C "$child" update-ref HEAD $commit &&
+       git -C "$child" update-server-info &&
+
+       git -c http.followredirects=true clone "$HTTPD_URL/dumb/alt-child.git"
+'
+
 test_done
index ba83e567e5ca49c1664fe13f447e2744d55bfb3a..ac74626a7b667c3723569cfbca9c77bc264c6728 100755 (executable)
@@ -43,7 +43,8 @@ test_expect_success 'clone http repository' '
        < Cache-Control: no-cache, max-age=0, must-revalidate
        < Content-Type: application/x-git-upload-pack-result
        EOF
-       GIT_TRACE_CURL=true git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
+       GIT_TRACE_CURL=true GIT_TEST_PROTOCOL_VERSION= \
+               git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
        test_cmp file clone/file &&
        tr '\''\015'\'' Q <err |
        sed -e "
@@ -80,12 +81,18 @@ test_expect_success 'clone http repository' '
                /^< Content-Length: /d
                /^< Transfer-Encoding: /d
        " >actual &&
-       sed -e "s/^> Accept-Encoding: .*/> Accept-Encoding: ENCODINGS/" \
-                       actual >actual.smudged &&
-       test_cmp exp actual.smudged &&
 
-       grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
-       test_line_count = 2 actual.gzip
+       # NEEDSWORK: If the overspecification of the expected result is reduced, we
+       # might be able to run this test in all protocol versions.
+       if test -z "$GIT_TEST_PROTOCOL_VERSION"
+       then
+               sed -e "s/^> Accept-Encoding: .*/> Accept-Encoding: ENCODINGS/" \
+                               actual >actual.smudged &&
+               test_cmp exp actual.smudged &&
+
+               grep "Accept-Encoding:.*gzip" actual >actual.gzip &&
+               test_line_count = 2 actual.gzip
+       fi
 '
 
 test_expect_success 'fetch changes via http' '
@@ -103,7 +110,13 @@ test_expect_success 'used upload-pack service' '
        GET  /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
        POST /smart/repo.git/git-upload-pack HTTP/1.1 200
        EOF
-       check_access_log exp
+
+       # NEEDSWORK: If the overspecification of the expected result is reduced, we
+       # might be able to run this test in all protocol versions.
+       if test -z "$GIT_TEST_PROTOCOL_VERSION"
+       then
+               check_access_log exp
+       fi
 '
 
 test_expect_success 'follow redirects (301)' '
@@ -151,7 +164,17 @@ test_expect_success 'clone from auth-only-for-objects repository' '
 
 test_expect_success 'no-op half-auth fetch does not require a password' '
        set_askpass wrong &&
-       git --git-dir=half-auth fetch &&
+
+       # NEEDSWORK: When using HTTP(S), protocol v0 supports a "half-auth"
+       # configuration with authentication required only when downloading
+       # objects and not refs, by having the HTTP server only require
+       # authentication for the "git-upload-pack" path and not "info/refs".
+       # This is not possible with protocol v2, since both objects and refs
+       # are obtained from the "git-upload-pack" path. A solution to this is
+       # to teach the server and client to be able to inline ls-refs requests
+       # as an Extra Parameter (see pack-protocol.txt), so that "info/refs"
+       # can serve refs, just like it does in protocol v0.
+       GIT_TEST_PROTOCOL_VERSION=0 git --git-dir=half-auth fetch &&
        expect_askpass none
 '
 
@@ -215,8 +238,14 @@ test_expect_success 'cookies stored in http.cookiefile when http.savecookies set
        git config http.cookiefile cookies.txt &&
        git config http.savecookies true &&
        git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
-       tail -3 cookies.txt | sort >cookies_tail.txt &&
-       test_cmp expect_cookies.txt cookies_tail.txt
+
+       # NEEDSWORK: If the overspecification of the expected result is reduced, we
+       # might be able to run this test in all protocol versions.
+       if test -z "$GIT_TEST_PROTOCOL_VERSION"
+       then
+               tail -3 cookies.txt | sort >cookies_tail.txt &&
+               test_cmp expect_cookies.txt cookies_tail.txt
+       fi
 '
 
 test_expect_success 'transfer.hiderefs works over smart-http' '
@@ -306,7 +335,10 @@ test_expect_success 'test allowreachablesha1inwant with unreachable' '
 
        git init --bare test_reachable.git &&
        git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" &&
-       test_must_fail git -C test_reachable.git fetch origin "$(git rev-parse HEAD)"
+       # Some protocol versions (e.g. 2) support fetching
+       # unadvertised objects, so restrict this test to v0.
+       test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+               git -C test_reachable.git fetch origin "$(git rev-parse HEAD)"
 '
 
 test_expect_success 'test allowanysha1inwant with unreachable' '
@@ -325,7 +357,10 @@ test_expect_success 'test allowanysha1inwant with unreachable' '
 
        git init --bare test_reachable.git &&
        git -C test_reachable.git remote add origin "$HTTPD_URL/smart/repo.git" &&
-       test_must_fail git -C test_reachable.git fetch origin "$(git rev-parse HEAD)" &&
+       # Some protocol versions (e.g. 2) support fetching
+       # unadvertised objects, so restrict this test to v0.
+       test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+               git -C test_reachable.git fetch origin "$(git rev-parse HEAD)" &&
 
        git -C "$server" config uploadpack.allowanysha1inwant 1 &&
        git -C test_reachable.git fetch origin "$(git rev-parse HEAD)"
@@ -434,5 +469,4 @@ test_expect_success 'server-side error detected' '
        grep "server-side error" actual
 '
 
-stop_httpd
 test_done
index 30857b84a8d5b5fdec59f775ab97293a7da4f2df..8a14be51a13280d3fccd340c027cd5f20bb941a5 100755 (executable)
@@ -127,7 +127,10 @@ test_expect_success 'use ref advertisement to filter out commits' '
        # not need to send any ancestors of "c3", but we still need to send "c3"
        # itself.
        test_config -C client fetch.negotiationalgorithm skipping &&
-       trace_fetch client origin to_fetch &&
+
+       # The ref advertisement itself is filtered when protocol v2 is used, so
+       # use v0.
+       GIT_TEST_PROTOCOL_VERSION= trace_fetch client origin to_fetch &&
        have_sent c5 c4^ c2side &&
        have_not_sent c4 c4^^ c4^^^
 '
index 1c49054595c23564cf762ac01ea0a1667a59f2f9..6eb0294978a03f35f5c246a6000a92fc377b02ed 100755 (executable)
@@ -132,5 +132,4 @@ test_expect_success 'server request log matches test results' '
        check_access_log exp
 '
 
-stop_httpd
 test_done
index 58ee7876853161087c256e56e687f0b341fde915..00fc612cacba4781975d066e01873e90e06050a8 100755 (executable)
@@ -198,5 +198,4 @@ test_expect_success FAKENC 'hostname interpolation works after LF-stripping' '
        test_cmp expect actual
 '
 
-stop_git_daemon
 test_done
index cd9283eeecd9d5d65ec5713b260d254679939fa8..5129b0724f703890c80c8dcbe7bd4347792963e3 100755 (executable)
@@ -23,6 +23,4 @@ test_expect_success 'failure in git-upload-pack is shown' '
        grep "< HTTP/1.1 500 Intentional Breakage" curl_log
 '
 
-stop_httpd
-
 test_done
index d6948cbdab03cf827d86511ade26366e89fda149..23854cab263d467ba189879add1fb6b408e00792 100755 (executable)
@@ -345,7 +345,7 @@ expect_ssh () {
 }
 
 test_expect_success 'clone myhost:src uses ssh' '
-       git clone myhost:src ssh-clone &&
+       GIT_TEST_PROTOCOL_VERSION=0 git clone myhost:src ssh-clone &&
        expect_ssh myhost src
 '
 
@@ -356,12 +356,12 @@ test_expect_success !MINGW,!CYGWIN 'clone local path foo:bar' '
 '
 
 test_expect_success 'bracketed hostnames are still ssh' '
-       git clone "[myhost:123]:src" ssh-bracket-clone &&
+       GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone &&
        expect_ssh "-p 123" myhost src
 '
 
 test_expect_success 'OpenSSH variant passes -4' '
-       git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
+       GIT_TEST_PROTOCOL_VERSION=0 git clone -4 "[myhost:123]:src" ssh-ipv4-clone &&
        expect_ssh "-4 -p 123" myhost src
 '
 
@@ -405,7 +405,7 @@ test_expect_success 'OpenSSH-like uplink is treated as ssh' '
        test_when_finished "rm -f \"\$TRASH_DIRECTORY/uplink\"" &&
        GIT_SSH="$TRASH_DIRECTORY/uplink" &&
        test_when_finished "GIT_SSH=\"\$TRASH_DIRECTORY/ssh\$X\"" &&
-       git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
+       GIT_TEST_PROTOCOL_VERSION=0 git clone "[myhost:123]:src" ssh-bracket-clone-sshlike-uplink &&
        expect_ssh "-p 123" myhost src
 '
 
@@ -444,14 +444,14 @@ test_expect_success 'single quoted plink.exe in GIT_SSH_COMMAND' '
 
 test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
-       GIT_SSH_VARIANT=ssh \
-       git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
+       GIT_TEST_PROTOCOL_VERSION=0 GIT_SSH_VARIANT=ssh \
+               git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
        expect_ssh "-p 123" myhost src
 '
 
 test_expect_success 'ssh.variant overrides plink detection' '
        copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
-       git -c ssh.variant=ssh \
+       GIT_TEST_PROTOCOL_VERSION=0 git -c ssh.variant=ssh \
                clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
        expect_ssh "-p 123" myhost src
 '
@@ -482,7 +482,7 @@ counter=0
 # $3 path
 test_clone_url () {
        counter=$(($counter + 1))
-       test_might_fail git clone "$1" tmp$counter &&
+       test_might_fail env GIT_TEST_PROTOCOL_VERSION=0 git clone "$1" tmp$counter &&
        shift &&
        expect_ssh "$@"
 }
@@ -733,6 +733,4 @@ test_expect_success 'partial clone using HTTP' '
        partial_clone "$HTTPD_DOCUMENT_ROOT_PATH/server" "$HTTPD_URL/smart/server"
 '
 
-stop_httpd
-
 test_done
index 9643acb1615fe0d28a7f0e386fa69a570a93dc8e..9a8f9886b3e2d82b5c10bf749d485f05bd364db6 100755 (executable)
@@ -331,6 +331,4 @@ test_expect_success 'when partial cloning, tolerate server not sending target of
        ! test -e "$HTTPD_ROOT_PATH/one-time-sed"
 '
 
-stop_httpd
-
 test_done
index ba86a44eb183921e844dff6f9d6a5bb27c7d5e91..7c9511c593c175b5065970b75319c6da03d68b31 100755 (executable)
@@ -4,6 +4,9 @@ test_description='test git wire-protocol transition'
 
 TEST_NO_CREATE_REPO=1
 
+# This is a protocol-specific test.
+GIT_TEST_PROTOCOL_VERSION=
+
 . ./test-lib.sh
 
 # Test protocol v1 with 'git://' transport
@@ -289,6 +292,4 @@ test_expect_success 'push with http:// using protocol v1' '
        grep "git< version 1" log
 '
 
-stop_httpd
-
 test_done
index e112b6086c96e4cb0ad7bc10dcd685e7570d496f..a0896593375803eb79782705a21d673c72172c83 100755 (executable)
@@ -687,6 +687,4 @@ test_expect_success 'when server does not send "ready", expect FLUSH' '
        test_i18ngrep "expected no other sections to be sent after no .ready." err
 '
 
-stop_httpd
-
 test_done
index f87b2f6df329975e243ab9c00b510ee4cc588d0b..b6a995e8579c91874d2850ca579f87bf93f40561 100755 (executable)
@@ -257,8 +257,6 @@ test_expect_success 'server loses a ref - ref in want' '
        test_i18ngrep "fatal: remote error: unknown ref refs/heads/raster" err
 '
 
-stop_httpd
-
 REPO="$(pwd)/repo"
 LOCAL_PRISTINE="$(pwd)/local_pristine"
 
index 872788ac8ca4ecc0a54c14a04e3f72661306f1d3..af8772fadaa0d590370ba8a28b33c5b455164312 100755 (executable)
@@ -34,5 +34,4 @@ test_expect_success 'http can be limited to from-user' '
                clone "$HTTPD_URL/smart-redir-perm/repo.git" redir.git
 '
 
-stop_httpd
 test_done
diff --git a/t/t7113-post-index-change-hook.sh b/t/t7113-post-index-change-hook.sh
new file mode 100755 (executable)
index 0000000..f011ad7
--- /dev/null
@@ -0,0 +1,144 @@
+#!/bin/sh
+
+test_description='post index change hook'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       mkdir -p dir1 &&
+       touch dir1/file1.txt &&
+       echo testing >dir1/file2.txt &&
+       git add . &&
+       git commit -m "initial"
+'
+
+test_expect_success 'test status, add, commit, others trigger hook without flags set' '
+       mkdir -p .git/hooks &&
+       write_script .git/hooks/post-index-change <<-\EOF &&
+               if test "$1" -eq 1; then
+                       echo "Invalid combination of flags passed to hook; updated_workdir is set." >testfailure
+                       exit 1
+               fi
+               if test "$2" -eq 1; then
+                       echo "Invalid combination of flags passed to hook; updated_skipworktree is set." >testfailure
+                       exit 1
+               fi
+               if test -f ".git/index.lock"; then
+                       echo ".git/index.lock exists" >testfailure
+                       exit 3
+               fi
+               if ! test -f ".git/index"; then
+                       echo ".git/index does not exist" >testfailure
+                       exit 3
+               fi
+               echo "success" >testsuccess
+       EOF
+       mkdir -p dir2 &&
+       touch dir2/file1.txt &&
+       touch dir2/file2.txt &&
+       : force index to be dirty &&
+       test-tool chmtime +60 dir1/file1.txt &&
+       git status &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       git add . &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       git commit -m "second" &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       git checkout -- dir1/file1.txt &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       git update-index &&
+       test_path_is_missing testsuccess &&
+       test_path_is_missing testfailure &&
+       git reset --soft &&
+       test_path_is_missing testsuccess &&
+       test_path_is_missing testfailure
+'
+
+test_expect_success 'test checkout and reset trigger the hook' '
+       write_script .git/hooks/post-index-change <<-\EOF &&
+               if test "$1" -eq 1 && test "$2" -eq 1; then
+                       echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
+                       exit 1
+               fi
+               if test "$1" -eq 0 && test "$2" -eq 0; then
+                       echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
+                       exit 2
+               fi
+               if test "$1" -eq 1; then
+                       if test -f ".git/index.lock"; then
+                               echo "updated_workdir set but .git/index.lock exists" >testfailure
+                               exit 3
+                       fi
+                       if ! test -f ".git/index"; then
+                               echo "updated_workdir set but .git/index does not exist" >testfailure
+                               exit 3
+                       fi
+               else
+                       echo "update_workdir should be set for checkout" >testfailure
+                       exit 4
+               fi
+               echo "success" >testsuccess
+       EOF
+       : force index to be dirty &&
+       test-tool chmtime +60 dir1/file1.txt &&
+       git checkout master &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       test-tool chmtime +60 dir1/file1.txt &&
+       git checkout HEAD &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       test-tool chmtime +60 dir1/file1.txt &&
+       git reset --hard &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       git checkout -B test &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure
+'
+
+test_expect_success 'test reset --mixed and update-index triggers the hook' '
+       write_script .git/hooks/post-index-change <<-\EOF &&
+               if test "$1" -eq 1 && test "$2" -eq 1; then
+                       echo "Invalid combination of flags passed to hook; updated_workdir and updated_skipworktree are both set." >testfailure
+                       exit 1
+               fi
+               if test "$1" -eq 0 && test "$2" -eq 0; then
+                       echo "Invalid combination of flags passed to hook; neither updated_workdir or updated_skipworktree are set." >testfailure
+                       exit 2
+               fi
+               if test "$2" -eq 1; then
+                       if test -f ".git/index.lock"; then
+                               echo "updated_skipworktree set but .git/index.lock exists" >testfailure
+                               exit 3
+                       fi
+                       if ! test -f ".git/index"; then
+                               echo "updated_skipworktree set but .git/index does not exist" >testfailure
+                               exit 3
+                       fi
+               else
+                       echo "updated_skipworktree should be set for reset --mixed and update-index" >testfailure
+                       exit 4
+               fi
+               echo "success" >testsuccess
+       EOF
+       : force index to be dirty &&
+       test-tool chmtime +60 dir1/file1.txt &&
+       git reset --mixed --quiet HEAD~1 &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       git hash-object -w --stdin <dir1/file2.txt >expect &&
+       git update-index --cacheinfo 100644 "$(cat expect)" dir1/file1.txt &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure &&
+       git update-index --skip-worktree dir1/file2.txt &&
+       git update-index --remove dir1/file2.txt &&
+       test_path_is_file testsuccess && rm -f testsuccess &&
+       test_path_is_missing testfailure
+'
+
+test_done
index e87164aa8ffdba169ba6f91af99ae3d28f28459b..c973278300a5c911845016ef1e23144f51ebcd7d 100755 (executable)
@@ -943,7 +943,10 @@ test_expect_success 'submodule update clone shallow submodule outside of depth'
                cd super3 &&
                sed -e "s#url = ../#url = file://$pwd/#" <.gitmodules >.gitmodules.tmp &&
                mv -f .gitmodules.tmp .gitmodules &&
-               test_must_fail git submodule update --init --depth=1 2>actual &&
+               # Some protocol versions (e.g. 2) support fetching
+               # unadvertised objects, so restrict this test to v0.
+               test_must_fail env GIT_TEST_PROTOCOL_VERSION= \
+                       git submodule update --init --depth=1 2>actual &&
                test_i18ngrep "Direct fetching of that commit failed." actual &&
                git -C ../submodule config uploadpack.allowReachableSHA1InWant true &&
                git submodule update --init --depth=1 >actual &&
index ef8b1b307851084308b1d8553c41c5e9dc61b836..a62736dce09f6b8623929b20cb3a70f56dfeabc0 100755 (executable)
@@ -84,7 +84,7 @@ test_expect_success 'status score=100%' '
        test_i18ngrep "deleted:" actual &&
        test_i18ngrep "new file:" actual &&
 
-       git status --find-rename=100% >actual &&
+       git status --find-renames=100% >actual &&
        test_i18ngrep "deleted:" actual &&
        test_i18ngrep "new file:" actual
 '
@@ -93,11 +93,11 @@ test_expect_success 'status score=01%' '
        git status -M=01% >actual &&
        test_i18ngrep "renamed:" actual &&
 
-       git status --find-rename=01% >actual &&
+       git status --find-renames=01% >actual &&
        test_i18ngrep "renamed:" actual
 '
 
-test_expect_success 'copies not overridden by find-rename' '
+test_expect_success 'copies not overridden by find-renames' '
        cp renamed copy &&
        git add copy &&
 
@@ -105,7 +105,7 @@ test_expect_success 'copies not overridden by find-rename' '
        test_i18ngrep "copied:" actual &&
        test_i18ngrep "renamed:" actual &&
 
-       git -c status.renames=copies status --find-rename=01% >actual &&
+       git -c status.renames=copies status --find-renames=01% >actual &&
        test_i18ngrep "copied:" actual &&
        test_i18ngrep "renamed:" actual
 '
index 43aa4161cf030b4c58ab2785e0bffc6635127a5f..2e1bb61b41f9278c7463d310e62242697d83a782 100755 (executable)
@@ -119,33 +119,33 @@ do
                test_cmp expected actual
        '
 
-       test_expect_success "grep -w $L (with --column, --invert)" '
+       test_expect_success "grep -w $L (with --column, --invert-match)" '
                {
                        echo ${HC}file:1:foo mmap bar
                        echo ${HC}file:1:foo_mmap bar
                        echo ${HC}file:1:foo_mmap bar mmap
                        echo ${HC}file:1:foo mmap bar_mmap
                } >expected &&
-               git grep --column --invert -w -e baz $H -- file >actual &&
+               git grep --column --invert-match -w -e baz $H -- file >actual &&
                test_cmp expected actual
        '
 
-       test_expect_success "grep $L (with --column, --invert, extended OR)" '
+       test_expect_success "grep $L (with --column, --invert-match, extended OR)" '
                {
                        echo ${HC}hello_world:6:HeLLo_world
                } >expected &&
-               git grep --column --invert -e ll --or --not -e _ $H -- hello_world \
+               git grep --column --invert-match -e ll --or --not -e _ $H -- hello_world \
                        >actual &&
                test_cmp expected actual
        '
 
-       test_expect_success "grep $L (with --column, --invert, extended AND)" '
+       test_expect_success "grep $L (with --column, --invert-match, extended AND)" '
                {
                        echo ${HC}hello_world:3:Hello world
                        echo ${HC}hello_world:3:Hello_world
                        echo ${HC}hello_world:6:HeLLo_world
                } >expected &&
-               git grep --column --invert --not -e _ --and --not -e ll $H -- hello_world \
+               git grep --column --invert-match --not -e _ --and --not -e ll $H -- hello_world \
                        >actual &&
                test_cmp expected actual
        '
@@ -1010,7 +1010,7 @@ test_expect_success 'outside of git repository' '
                        echo ".gitignore:.*o*" &&
                        cat ../expect.full
                } >../expect.with.ignored &&
-               git grep --no-index --no-exclude o >../actual.full &&
+               git grep --no-index --no-exclude-standard o >../actual.full &&
                test_cmp ../expect.with.ignored ../actual.full
        )
 '
@@ -1051,7 +1051,7 @@ test_expect_success 'outside of git repository with fallbackToNoIndex' '
                        echo ".gitignore:.*o*" &&
                        cat ../expect.full
                } >../expect.with.ignored &&
-               git -c grep.fallbackToNoIndex grep --no-exclude o >../actual.full &&
+               git -c grep.fallbackToNoIndex grep --no-exclude-standard o >../actual.full &&
                test_cmp ../expect.with.ignored ../actual.full
        )
 '
index 64bb495834698c8438c8b8dde488873072abc1fd..9b44a44bc1f917e2a05956e3300e2cb673a3fec3 100755 (executable)
@@ -120,6 +120,4 @@ test_expect_success !MINGW,!UTF8_NFD_TO_NFC 'svn.pathnameencoding=cp932 rename o
        git svn dcommit
 '
 
-stop_httpd
-
 test_done
index 41a026637fa03174765d4e3facc3815bbf538e29..a159ff96b71882362eaa7d7856289561601674cf 100755 (executable)
@@ -87,6 +87,4 @@ test_expect_success 'test dcommit to trailing_dotlock branch' '
        )
        '
 
-stop_httpd
-
 test_done
index b28a1741e3f52296dcab35ed6d51fa01c2628652..40b714df31a20f5b6908ca69dd229d624bc775cc 100755 (executable)
@@ -74,6 +74,4 @@ test_expect_success 'test clone -s with unescaped space' '
        )
 '
 
-stop_httpd
-
 test_done
index 9ee23be64003ab22277c80a6c4b364c8cc36f902..a30730502d859408a3d59a1859e55a07eac0c6ce 100755 (executable)
@@ -26,6 +26,4 @@ test_expect_success 'clone trunk with "-r HEAD"' '
        ( cd g && git rev-parse --symbolic --verify HEAD )
 '
 
-stop_httpd
-
 test_done
index 729cd25770177aa52bade1100bb4ddae1456d269..5856563068c71280065c28d294a7c0a1149f6008 100755 (executable)
@@ -326,8 +326,4 @@ test_expect_success 'submit from worktree' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 6a86d6996b97de0bed0503021b76b33db8441029..38d6b9043b2039d97cdb634ac86658c45ec36abd 100755 (executable)
@@ -151,7 +151,7 @@ test_expect_success 'import depot, branch detection, branchList branch definitio
 '
 
 test_expect_success 'restart p4d' '
-       kill_p4d &&
+       stop_and_cleanup_p4d &&
        start_p4d
 '
 
@@ -505,7 +505,7 @@ test_expect_success 'use-client-spec detect-branches skips files in branches' '
 '
 
 test_expect_success 'restart p4d' '
-       kill_p4d &&
+       stop_and_cleanup_p4d &&
        start_p4d
 '
 
@@ -610,8 +610,4 @@ test_expect_success 'Update a file in git side and submit to P4 using client vie
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 9978352d7828dbd63d44d172b91193a20291c4d1..94edebe272691a72768cea14c1185c26113aad14 100755 (executable)
@@ -333,8 +333,4 @@ test_expect_success SYMLINKS 'empty symlink target' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index d5c367510049607ce33db73ffc1869a0652fe663..2913277013da56e98b9ad8b6ec3ea47ccbace103 100755 (executable)
@@ -105,8 +105,4 @@ test_expect_success 'branch with shell char' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index e30f80e617674967b1d474c1485a6570f1ef2903..32364571063d4c6001105d0929ca81fe50e2a87c 100755 (executable)
@@ -108,8 +108,4 @@ test_expect_failure 'two labels on the same changelist' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 5fbf904dc8f4df7de522d0eecbca133d7299259d..90ef647db7e610b825cc92d51ee2a5379dcf05b3 100755 (executable)
@@ -98,8 +98,4 @@ test_expect_success 'no config, edited' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 3f5291b85752e7bc78901da18c2bfa654134eb59..4e794a01bf556f97f94eaabf8cc259f333c75049 100755 (executable)
@@ -300,9 +300,4 @@ test_expect_success 'use --git-dir option and GIT_DIR' '
        test_path_is_file "$git"/cli_file2.t
 '
 
-
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 850d97911966fe78f22035dab78ac0556a82fd99..eaaae414a1ff22d127a51f047716800a8585d87a 100755 (executable)
@@ -593,8 +593,4 @@ test_expect_success 'update a shelve involving moved and copied files' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 11d2b5102cde384ad11540610a52f62c4732aa8c..58a9b3b71e6d88dfc9e3086fb515362e7c31e34a 100755 (executable)
@@ -83,8 +83,4 @@ test_expect_success SYMLINKS 'p4 client root symlink should stay symbolic' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 897b3c3034efcab3af88d8eddc4670e274361dd5..3cff1fce1b7464826412ece52ef394a551dce65e 100755 (executable)
@@ -836,8 +836,4 @@ test_expect_success 'quotes on both sides' '
        git_verify "cdir 1/file11" "cdir 1/file12"
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index cc53debe1955ece317f994b0e76f2b8470b0f4f6..57b533dc6fbaa9d9b82bdaf300b46a426c7b43b7 100755 (executable)
@@ -360,8 +360,4 @@ test_expect_failure 'Add keywords in git which do not match the default p4 value
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 602b0a5d5ceafcac6439491b463fee7867e7036f..c1446f26aba59e19b1610652a1db449e8bddca92 100755 (executable)
@@ -191,7 +191,7 @@ test_expect_success 'tag that cannot be exported' '
        (
                cd "$cli" &&
                p4 sync ... &&
-               !(p4 labels | grep GIT_TAG_ON_A_BRANCH)
+               ! p4 labels | grep GIT_TAG_ON_A_BRANCH
        )
 '
 
@@ -259,9 +259,4 @@ test_expect_success 'importing labels with missing revisions' '
        )
 '
 
-
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 0206771fbb91b734a626e628b21786805ebcfa22..254a7c244698a0140d6f5605d3013f72d9775310 100755 (executable)
@@ -211,8 +211,4 @@ test_expect_success 'wildcard files requiring keyword scrub' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 783c6ad1653142d174e4ddc6d49e1f631121be51..fd018c87a80636d7bb286fe596f299bbbbe6828b 100755 (executable)
@@ -138,8 +138,4 @@ test_expect_success 'not preserving user with mixed authorship' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 60baa06e27a0a6c45f580738e60f7fe8fa65373a..468767cbf4b93eb20f209fc25b46cc0f53d5b907 100755 (executable)
@@ -242,8 +242,4 @@ test_expect_success P4D_HAVE_CONFIGURABLE_RUN_MOVE_ALLOW \
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index eaf03a656329c7b4b9d0ed40c839232a81b310ac..9779dc0d11f33b6d8a6c5b8d8ffded834eddd8b2 100755 (executable)
@@ -422,8 +422,4 @@ test_expect_success 'cleanup chmod after submit cancel' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index d048bd33fa3c94402eb750b65553f72b955aa25f..932841003cfc4e3f6273087f8567d6c6a660bb0b 100755 (executable)
@@ -138,8 +138,4 @@ test_expect_failure 'move with lock taken' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index aac568eadfcab6198877d211d3e706cead68e302..96d25f0c02ccfb04391487b550a702751bf22a11 100755 (executable)
@@ -64,8 +64,4 @@ test_expect_success 'clone, then sync with exclude' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index ce7cb22ad37165c6976b4484f6b39a49747d4485..0db7ab99184add2c500f821659b09cc833c6a225 100755 (executable)
@@ -146,8 +146,4 @@ test_expect_success 'Clone repo with self-sizing block size' '
        test_line_count \> 10 log
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index d808c008c11700badffdcbbd34faba39428a47f1..600ce1e0b0d7edf193291a6a25450c92a5888475 100755 (executable)
@@ -53,8 +53,4 @@ test_expect_failure 'Clone UC repo with lc name' '
        test_must_fail git p4 clone //depot/uc/...
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 3c22f74bd436b7c6d94d5c29d5b2e3e3510d58e6..fa1bba1dd93614c8ad5c7f4a37c5b612440fd0eb 100755 (executable)
@@ -31,8 +31,4 @@ test_expect_success 'EDITOR with options' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 81e46acfa8ba10b8d0051acc1e7b19dc6b1de365..ef80f1690bcb9ad153df900e277ef48ce8e4e62e 100755 (executable)
@@ -193,8 +193,4 @@ test_expect_success 'Add a new file and clone path with new file (ignorecase)' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index c78477c19b4330f990c0cdd7d96997b8ed2ed02c..1bf7635016f54385912e7bd830eada93810a4bfc 100755 (executable)
@@ -67,8 +67,4 @@ test_expect_success 'Delete iso8859-1 encoded paths and clone' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 1f2dc369bfa96e767741484b421f42b56b3c0a18..88b76dc4d6c26fc99e32c91a11b2eb5bb7118e2c 100755 (executable)
@@ -185,8 +185,4 @@ test_expect_success 'Run git p4 submit in repo configured with large file system
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index ed80ca858c8f8352bd5a26b9a30e0c182d102ee9..a28dbbdd566ca69212f23958056d95b735a0127c 100755 (executable)
@@ -287,8 +287,4 @@ test_expect_success 'Add big files to repo and store files in LFS based on compr
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 1551845dc178e3cc57d31ac3e66f60db921be874..f049ff8229c6d38053dd3b3842e898e8473eaeb3 100755 (executable)
@@ -43,8 +43,4 @@ test_expect_failure 'clone depot with invalid UTF-16 file in non-verbose mode' '
        git p4 clone --dest="$git" //depot
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index fa8b9daf1fafba7498583e916cfea52d3c7207f5..fd64afe064e5a937b4a15d18f70e87cd524067c8 100755 (executable)
@@ -127,8 +127,4 @@ test_expect_success 'Clone repo subdir with all history' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 7433998f4779c7d1691e8c6fbb31fed3224c355d..d3670bd7a24dbf3cdc350939d6d130bd662f3e73 100755 (executable)
@@ -59,8 +59,4 @@ test_expect_success SYMLINKS 'change symbolic link to file' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index e20395c89f3dbc461516e22339f9fef47bd3426e..ca6c2942bdf200942cc4122e6172a120e0f1d1c1 100755 (executable)
@@ -54,8 +54,4 @@ test_expect_success 'Clone repo root path with all history' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 971aeeea1fb69ff5b73109d8b9496b5574317cd5..88cfb1fcd3f0a1401fd1cdb10f1fe697a5c4aaa3 100755 (executable)
@@ -92,8 +92,4 @@ test_expect_success 'check log message of changelist with more jobs' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 2ad1b0810df81ecaf5197e4cf860b3524d557c09..3fb6960c18fc0c2eb549f09c66d92b3f39822653 100755 (executable)
@@ -36,8 +36,4 @@ test_expect_success 'symlinked directory' '
 
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index be44c9751aefa5368d6f05d7b1c29f0c8354095f..d743ca33ee6ab29f97d328756663803824f105f5 100755 (executable)
@@ -96,8 +96,4 @@ test_expect_success 'submit description with extra info lines from verbose p4 ch
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
 test_done
index 41c09f11f4bf44ece085f952e8782d4dfe3fd169..1286a5b824b77d71061fb890d9f528b46ecc087d 100755 (executable)
@@ -174,8 +174,5 @@ test_expect_success 'unshelve specifying the origin' '
                test_path_is_file file_to_shelve
        )
 '
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
 
 test_done
index 47b312e1c9705af6f6ac6bb55ee81cb847f53d0c..e22369ccdf5f15840546269d0cdef9e8a54ce698 100755 (executable)
@@ -45,9 +45,4 @@ test_expect_success 'ticket logged out' '
        )
 '
 
-test_expect_success 'kill p4d' '
-       kill_p4d
-'
-
-
 test_done
index f5e21bf970f4dc44c7fa30d54134a15cf343ffc1..43cf313a1c09ad8fb223ba775d5548c3f00c339c 100755 (executable)
@@ -1484,6 +1484,12 @@ test_expect_success 'git --help completion' '
        test_completion "git --help core" "core-tutorial "
 '
 
+test_expect_success 'completion.commands removes multiple commands' '
+       test_config completion.commands "-cherry -mergetool" &&
+       git --list-cmds=list-mainporcelain,list-complete,config >out &&
+       ! grep -E "^(cherry|mergetool)$" out
+'
+
 test_expect_success 'setup for integration tests' '
        echo content >file1 &&
        echo more >file2 &&
index 80402a428f7735a031658a904d4f623f38e43464..788ea1f18b99c5edacd9ae0ee81310141c799c6e 100644 (file)
@@ -593,6 +593,15 @@ test_dir_is_empty () {
        fi
 }
 
+# Check if the file exists and has a size greater than zero
+test_file_not_empty () {
+       if ! test -s "$1"
+       then
+               echo "'$1' is not a non-empty file."
+               false
+       fi
+}
+
 test_path_is_missing () {
        if test -e "$1"
        then
@@ -934,6 +943,34 @@ test_when_finished () {
                } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
 }
 
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test script, e.g. to stop a daemon:
+#
+#      test_expect_success 'test git daemon' '
+#              git daemon &
+#              daemon_pid=$! &&
+#              test_atexit 'kill $daemon_pid' &&
+#              hello world
+#      '
+#
+# The commands will be executed before the trash directory is removed,
+# i.e. the atexit commands will still be able to access any pidfiles or
+# socket files.
+#
+# Note that these commands will be run even when a test script run
+# with '--immediate' fails.  Be careful with your atexit commands to
+# minimize any changes to the failed state.
+
+test_atexit () {
+       # We cannot detect when we are in a subshell in general, but by
+       # doing so on Bash is better than nothing (the test will
+       # silently pass on other shells).
+       test "${BASH_SUBSHELL-0}" = 0 ||
+       error "bug in test script: test_atexit does nothing in a subshell"
+       test_atexit_cleanup="{ $*
+               } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
+}
+
 # Most tests can use the created repository, but some may need to create more.
 # Usage: test_create_repo <directory>
 test_create_repo () {
index 562c57e6858b79aff074fcb7ab8c65fa2f110bc0..38b157075a79bb7674394f4856a9781e2db22cae 100644 (file)
@@ -57,6 +57,13 @@ fi
 . "$GIT_BUILD_DIR"/GIT-BUILD-OPTIONS
 export PERL_PATH SHELL_PATH
 
+# Disallow the use of abbreviated options in the test suite by default
+if test -z "${GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS}"
+then
+       GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS=true
+       export GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS
+fi
+
 ################################################################
 # It appears that people try to run tests without building...
 "${GIT_TEST_INSTALLED:-$GIT_BUILD_DIR}/git$X" >/dev/null
@@ -627,6 +634,10 @@ test_external_has_tap=0
 
 die () {
        code=$?
+       # This is responsible for running the atexit commands even when a
+       # test script run with '--immediate' fails, or when the user hits
+       # ctrl-C, i.e. when 'test_done' is not invoked at all.
+       test_atexit_handler || code=$?
        if test -n "$GIT_EXIT_OK"
        then
                exit $code
@@ -638,7 +649,10 @@ die () {
 
 GIT_EXIT_OK=
 trap 'die' EXIT
-trap 'exit $?' INT TERM HUP
+# Disable '-x' tracing, because with some shells, notably dash, it
+# prevents running the cleanup commands when a test script run with
+# '--verbose-log -x' is interrupted.
+trap '{ code=$?; set +x; } 2>/dev/null; exit $code' INT TERM HUP
 
 # The user-facing functions are loaded from a separate file so that
 # test_perf subshells can have them too
@@ -1049,9 +1063,28 @@ write_junit_xml_testcase () {
        junit_have_testcase=t
 }
 
+test_atexit_cleanup=:
+test_atexit_handler () {
+       # In a succeeding test script 'test_atexit_handler' is invoked
+       # twice: first from 'test_done', then from 'die' in the trap on
+       # EXIT.
+       # This condition and resetting 'test_atexit_cleanup' below makes
+       # sure that the registered cleanup commands are run only once.
+       test : != "$test_atexit_cleanup" || return 0
+
+       setup_malloc_check
+       test_eval_ "$test_atexit_cleanup"
+       test_atexit_cleanup=:
+       teardown_malloc_check
+}
+
 test_done () {
        GIT_EXIT_OK=t
 
+       # Run the atexit commands _before_ the trash directory is
+       # removed, so the commands can access pidfiles and socket files.
+       test_atexit_handler
+
        if test -n "$write_junit_xml" && test -n "$junit_xml_path"
        then
                test -n "$junit_have_testcase" || {
index ccccd4ef0919eebe1eff5834df3016778b0459d1..8bbad568871dc2a8526d47ae4cb9f36f49fcb7a3 100644 (file)
--- a/trace2.c
+++ b/trace2.c
@@ -548,10 +548,14 @@ void trace2_region_enter_printf_va_fl(const char *file, int line,
 }
 
 void trace2_region_enter_fl(const char *file, int line, const char *category,
-                           const char *label, const struct repository *repo)
+                           const char *label, const struct repository *repo, ...)
 {
+       va_list ap;
+       va_start(ap, repo);
        trace2_region_enter_printf_va_fl(file, line, category, label, repo,
-                                        NULL, NULL);
+                                        NULL, ap);
+       va_end(ap);
+
 }
 
 void trace2_region_enter_printf_fl(const char *file, int line,
@@ -621,10 +625,13 @@ void trace2_region_leave_printf_va_fl(const char *file, int line,
 }
 
 void trace2_region_leave_fl(const char *file, int line, const char *category,
-                           const char *label, const struct repository *repo)
+                           const char *label, const struct repository *repo, ...)
 {
+       va_list ap;
+       va_start(ap, repo);
        trace2_region_leave_printf_va_fl(file, line, category, label, repo,
-                                        NULL, NULL);
+                                        NULL, ap);
+       va_end(ap);
 }
 
 void trace2_region_leave_printf_fl(const char *file, int line,
index ae5020d0e66e08d86e0eb1398bce884c29de4487..b330a54a89a08c2e1f764db6708a8f55a7479f3b 100644 (file)
--- a/trace2.h
+++ b/trace2.h
@@ -238,7 +238,7 @@ void trace2_def_repo_fl(const char *file, int line, struct repository *repo);
  * on this thread.
  */
 void trace2_region_enter_fl(const char *file, int line, const char *category,
-                           const char *label, const struct repository *repo);
+                           const char *label, const struct repository *repo, ...);
 
 #define trace2_region_enter(category, label, repo) \
        trace2_region_enter_fl(__FILE__, __LINE__, (category), (label), (repo))
@@ -278,7 +278,7 @@ void trace2_region_enter_printf(const char *category, const char *label,
  * in this nesting level.
  */
 void trace2_region_leave_fl(const char *file, int line, const char *category,
-                           const char *label, const struct repository *repo);
+                           const char *label, const struct repository *repo, ...);
 
 #define trace2_region_leave(category, label, repo) \
        trace2_region_leave_fl(__FILE__, __LINE__, (category), (label), (repo))
index 107cb5317d2270ccf8b54727fb55f4526a503c24..1cf4f62441c9307d610ebb0274ddb272ec2b8f01 100644 (file)
@@ -190,7 +190,7 @@ static void fn_atexit(uint64_t us_elapsed_absolute, int code)
 static void maybe_add_string_va(struct json_writer *jw, const char *field_name,
                                const char *fmt, va_list ap)
 {
-       if (fmt && *fmt && ap) {
+       if (fmt && *fmt) {
                va_list copy_ap;
                struct strbuf buf = STRBUF_INIT;
 
index 547183d5b64169acfc1e8f9b1f62501de36240ef..1a07d70abd6de281fc2a865f026fbb2e426adf96 100644 (file)
@@ -126,7 +126,7 @@ static void fn_atexit(uint64_t us_elapsed_absolute, int code)
 static void maybe_append_string_va(struct strbuf *buf, const char *fmt,
                                   va_list ap)
 {
-       if (fmt && *fmt && ap) {
+       if (fmt && *fmt) {
                va_list copy_ap;
 
                va_copy(copy_ap, ap);
index f0746fcf868ccab0408333bb954e7d531843af7d..2a866d701b201d36521cbd7d860d23f378a0f5b7 100644 (file)
@@ -211,7 +211,7 @@ static void fn_atexit(uint64_t us_elapsed_absolute, int code)
 static void maybe_append_string_va(struct strbuf *buf, const char *fmt,
                                   va_list ap)
 {
-       if (fmt && *fmt && ap) {
+       if (fmt && *fmt) {
                va_list copy_ap;
 
                va_copy(copy_ap, ap);
index 7c643760f81abb9efd1208e9a6e885e5d6ffd4ea..93a48f3bf8429ebb9b40f6a079e6ef0c6d0edcd8 100644 (file)
@@ -89,8 +89,7 @@ static const struct interval zero_width[] = {
 { 0x0E34, 0x0E3A },
 { 0x0E47, 0x0E4E },
 { 0x0EB1, 0x0EB1 },
-{ 0x0EB4, 0x0EB9 },
-{ 0x0EBB, 0x0EBC },
+{ 0x0EB4, 0x0EBC },
 { 0x0EC8, 0x0ECD },
 { 0x0F18, 0x0F19 },
 { 0x0F35, 0x0F35 },
@@ -191,7 +190,7 @@ static const struct interval zero_width[] = {
 { 0xA980, 0xA982 },
 { 0xA9B3, 0xA9B3 },
 { 0xA9B6, 0xA9B9 },
-{ 0xA9BC, 0xA9BC },
+{ 0xA9BC, 0xA9BD },
 { 0xA9E5, 0xA9E5 },
 { 0xAA29, 0xAA2E },
 { 0xAA31, 0xAA32 },
@@ -274,6 +273,9 @@ static const struct interval zero_width[] = {
 { 0x11727, 0x1172B },
 { 0x1182F, 0x11837 },
 { 0x11839, 0x1183A },
+{ 0x119D4, 0x119D7 },
+{ 0x119DA, 0x119DB },
+{ 0x119E0, 0x119E0 },
 { 0x11A01, 0x11A0A },
 { 0x11A33, 0x11A38 },
 { 0x11A3B, 0x11A3E },
@@ -298,8 +300,10 @@ static const struct interval zero_width[] = {
 { 0x11D95, 0x11D95 },
 { 0x11D97, 0x11D97 },
 { 0x11EF3, 0x11EF4 },
+{ 0x13430, 0x13438 },
 { 0x16AF0, 0x16AF4 },
 { 0x16B30, 0x16B36 },
+{ 0x16F4F, 0x16F4F },
 { 0x16F8F, 0x16F92 },
 { 0x1BC9D, 0x1BC9E },
 { 0x1BCA0, 0x1BCA3 },
@@ -319,6 +323,8 @@ static const struct interval zero_width[] = {
 { 0x1E01B, 0x1E021 },
 { 0x1E023, 0x1E024 },
 { 0x1E026, 0x1E02A },
+{ 0x1E130, 0x1E136 },
+{ 0x1E2EC, 0x1E2EF },
 { 0x1E8D0, 0x1E8D6 },
 { 0x1E944, 0x1E94A },
 { 0xE0001, 0xE0001 },
@@ -387,10 +393,12 @@ static const struct interval double_width[] = {
 { 0xFE68, 0xFE6B },
 { 0xFF01, 0xFF60 },
 { 0xFFE0, 0xFFE6 },
-{ 0x16FE0, 0x16FE1 },
-{ 0x17000, 0x187F1 },
+{ 0x16FE0, 0x16FE3 },
+{ 0x17000, 0x187F7 },
 { 0x18800, 0x18AF2 },
 { 0x1B000, 0x1B11E },
+{ 0x1B150, 0x1B152 },
+{ 0x1B164, 0x1B167 },
 { 0x1B170, 0x1B2FB },
 { 0x1F004, 0x1F004 },
 { 0x1F0CF, 0x1F0CF },
@@ -422,16 +430,20 @@ static const struct interval double_width[] = {
 { 0x1F680, 0x1F6C5 },
 { 0x1F6CC, 0x1F6CC },
 { 0x1F6D0, 0x1F6D2 },
+{ 0x1F6D5, 0x1F6D5 },
 { 0x1F6EB, 0x1F6EC },
-{ 0x1F6F4, 0x1F6F9 },
-{ 0x1F910, 0x1F93E },
-{ 0x1F940, 0x1F970 },
+{ 0x1F6F4, 0x1F6FA },
+{ 0x1F7E0, 0x1F7EB },
+{ 0x1F90D, 0x1F971 },
 { 0x1F973, 0x1F976 },
-{ 0x1F97A, 0x1F97A },
-{ 0x1F97C, 0x1F9A2 },
-{ 0x1F9B0, 0x1F9B9 },
-{ 0x1F9C0, 0x1F9C2 },
-{ 0x1F9D0, 0x1F9FF },
+{ 0x1F97A, 0x1F9A2 },
+{ 0x1F9A5, 0x1F9AA },
+{ 0x1F9AE, 0x1F9CA },
+{ 0x1F9CD, 0x1F9FF },
+{ 0x1FA70, 0x1FA73 },
+{ 0x1FA78, 0x1FA7A },
+{ 0x1FA80, 0x1FA82 },
+{ 0x1FA90, 0x1FA95 },
 { 0x20000, 0x2FFFD },
 { 0x30000, 0x3FFFD }
 };
index 5f65ff580403fe0171e30a173f541fce8d4731de..5f4325278a0de15091a3437133ebdf34bff7e43e 100644 (file)
@@ -1617,6 +1617,8 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
                                                  WRITE_TREE_SILENT |
                                                  WRITE_TREE_REPAIR);
                }
+
+               o->result.updated_workdir = 1;
                discard_index(o->dst_index);
                *o->dst_index = o->result;
        } else {
@@ -2383,7 +2385,7 @@ int oneway_merge(const struct cache_entry * const *src,
                if (o->update && S_ISGITLINK(old->ce_mode) &&
                    should_update_submodules() && !verify_uptodate(old, o))
                        update |= CE_UPDATE;
-               add_entry(o, old, update, 0);
+               add_entry(o, old, update, CE_STAGEMASK);
                return 0;
        }
        return merged_entry(a, old, o);