]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jk/clang-sanitizer-fixes'
authorJunio C Hamano <gitster@pobox.com>
Wed, 12 Feb 2020 20:41:36 +0000 (12:41 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 12 Feb 2020 20:41:36 +0000 (12:41 -0800)
C pedantry ;-) fix.

* jk/clang-sanitizer-fixes:
  obstack: avoid computing offsets from NULL pointer
  xdiff: avoid computing non-zero offset from NULL pointer
  avoid computing zero offsets from NULL pointer
  merge-recursive: use subtraction to flip stage
  merge-recursive: silence -Wxor-used-as-pow warning

113 files changed:
.editorconfig
.mailmap
Documentation/RelNotes/2.26.0.txt [new file with mode: 0644]
Documentation/config/gpg.txt
Documentation/config/http.txt
Documentation/config/user.txt
Documentation/fetch-options.txt
Documentation/git-commit-tree.txt
Documentation/git-commit.txt
Documentation/git-sparse-checkout.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitcore-tutorial.txt
Documentation/pretty-formats.txt
GIT-VERSION-GEN
Makefile
RelNotes
add-interactive.c
add-interactive.h
add-patch.c
builtin/add.c
builtin/checkout.c
builtin/commit.c
builtin/merge.c
builtin/pull.c
builtin/stash.c
ci/run-build-and-tests.sh
commit.c
commit.h
compat/regex/regex.h
compat/terminal.c
compat/terminal.h
contrib/completion/git-completion.bash
contrib/credential/netrc/.gitignore [new file with mode: 0644]
contrib/credential/netrc/Makefile
contrib/credential/netrc/git-credential-netrc.perl [moved from contrib/credential/netrc/git-credential-netrc with 99% similarity]
dir.c
git-legacy-stash.sh
git-submodule.sh
gpg-interface.c
gpg-interface.h
graph.c
object-store.h
pretty.c
run-command.c
setup.c
sha1-file.c
string-list.h
submodule.c
t/check-non-portable-shell.pl
t/helper/test-parse-pathspec-file.c [new file with mode: 0644]
t/helper/test-tool.c
t/helper/test-tool.h
t/lib-git-p4.sh
t/t0000-basic.sh
t/t0003-attributes.sh
t/t0020-crlf.sh
t/t0067-parse_pathspec_file.sh [new file with mode: 0755]
t/t1306-xdg-files.sh
t/t1307-config-blob.sh
t/t1409-avoid-packing-refs.sh
t/t1501-work-tree.sh
t/t1506-rev-parse-diagnosis.sh
t/t1507-rev-parse-upstream.sh
t/t2024-checkout-dwim.sh
t/t2026-checkout-pathspec-file.sh
t/t2070-restore.sh
t/t2072-restore-pathspec-file.sh
t/t2405-worktree-submodule.sh [new file with mode: 0755]
t/t3600-rm.sh
t/t3701-add-interactive.sh
t/t3704-add-pathspec-file.sh
t/t4018-diff-funcname.sh
t/t4054-diff-bogus-tree.sh
t/t4066-diff-emit-delay.sh
t/t4134-apply-submodule.sh
t/t4200-rerere.sh
t/t4202-log.sh
t/t4204-patch-id.sh
t/t4215-log-skewed-merges.sh
t/t4300-merge-tree.sh
t/t5318-commit-graph.sh
t/t5319-multi-pack-index.sh
t/t5324-split-commit-graph.sh
t/t5504-fetch-receive-strict.sh
t/t5510-fetch.sh
t/t5512-ls-remote.sh
t/t5530-upload-pack-error.sh
t/t5537-fetch-shallow.sh
t/t5540-http-push-webdav.sh
t/t5552-skipping-fetch-negotiator.sh
t/t5562-http-backend-content-length.sh
t/t5573-pull-verify-signatures.sh
t/t5601-clone.sh
t/t5604-clone-reference.sh
t/t5616-partial-clone.sh
t/t5702-protocol-v2.sh
t/t6025-merge-symlinks.sh
t/t7030-verify-tag.sh
t/t7107-reset-pathspec-file.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7410-submodule-checkout-to.sh [deleted file]
t/t7500-commit-template-squash-signoff.sh
t/t7510-signed-commit.sh
t/t7526-commit-pathspec-file.sh
t/t7612-merge-verify-signatures.sh
t/t7800-difftool.sh
t/t9902-completion.sh
templates/hooks--pre-commit.sample
templates/hooks--update.sample
transport.c
unpack-trees.c

index 42cdc4bbfb05934bb9c3ed2fe0e0d45212c32d7a..f9d819623d832113014dd5d5366e8ee44ac9666a 100644 (file)
@@ -4,7 +4,7 @@ insert_final_newline = true
 
 # The settings for C (*.c and *.h) files are mirrored in .clang-format.  Keep
 # them in sync.
-[*.{c,h,sh,perl,pl,pm}]
+[*.{c,h,sh,perl,pl,pm,txt}]
 indent_style = tab
 tab_width = 8
 
index fbef813f24f13dfff55a4859017009a58fbb77c6..73fd92e192bd348dd64965fd33feb9d61b11b957 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -59,6 +59,7 @@ David S. Miller <davem@davemloft.net>
 David Turner <novalis@novalis.org> <dturner@twopensource.com>
 David Turner <novalis@novalis.org> <dturner@twosigma.com>
 Derrick Stolee <dstolee@microsoft.com> <stolee@gmail.com>
+Derrick Stolee <dstolee@microsoft.com> Derrick Stolee via GitGitGadget <gitgitgadget@gmail.com>
 Deskin Miller <deskinm@umich.edu>
 Đoàn Trần Công Danh <congdanhqx@gmail.com> Doan Tran Cong Danh
 Dirk Süsserott <newsletter@dirk.my1.cc>
@@ -288,6 +289,7 @@ William Pursell <bill.pursell@gmail.com>
 YONETANI Tomokazu <y0n3t4n1@gmail.com> <qhwt+git@les.ath.cx>
 YONETANI Tomokazu <y0n3t4n1@gmail.com> <y0netan1@dragonflybsd.org>
 YOSHIFUJI Hideaki <yoshfuji@linux-ipv6.org>
+Yi-Jyun Pan <pan93412@gmail.com>
 # the two anonymous contributors are different persons:
 anonymous <linux@horizon.com>
 anonymous <linux@horizon.net>
diff --git a/Documentation/RelNotes/2.26.0.txt b/Documentation/RelNotes/2.26.0.txt
new file mode 100644 (file)
index 0000000..8ef5ba3
--- /dev/null
@@ -0,0 +1,131 @@
+Git 2.26 Release Notes
+======================
+
+Updates since v2.25
+-------------------
+
+UI, Workflows & Features
+
+ * Sample credential helper for using .netrc has been updated to work
+   out of the box.
+
+ * gpg.minTrustLevel configuration variable has been introduced to
+   tell various signature verification codepaths the required minimum
+   trust level.
+
+ * The command line completion (in contrib/) learned to complete
+   subcommands and arguments to "git worktree".
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * Tell .editorconfig that in this project, *.txt files are indented
+   with tabs.
+
+ * The test-lint machinery knew to check "VAR=VAL shell_function"
+   construct, but did not check "VAR= shell_funciton", which has been
+   corrected.
+
+ * Replace "git config --bool" calls with "git config --type=bool" in
+   sample templates.
+
+ * The effort to move "git-add--interactive" to C continues.
+
+ * Improve error message generation for "git submodule add".
+
+ * Preparation of test scripts for the day when the object names will
+   use SHA-256 continues.
+
+
+Fixes since v2.25
+-----------------
+
+ * "git commit" gives output similar to "git status" when there is
+   nothing to commit, but without honoring the advise.statusHints
+   configuration variable, which has been corrected.
+   (merge 5c4f55f1f6 hw/commit-advise-while-rejecting later to maint).
+
+ * has_object_file() said "no" given an object registered to the
+   system via pretend_object_file(), making it inconsistent with
+   read_object_file(), causing lazy fetch to attempt fetching an
+   empty tree from promisor remotes.
+   (merge 9c8a294a1a jt/sha1-file-remove-oi-skip-cached later to maint).
+
+ * Complete an update to tutorial that encourages "git switch" over
+   "git checkout" that was done only half-way.
+   (merge 1a7e454dd6 hw/tutorial-favor-switch-over-checkout later to maint).
+
+ * C pedantry ;-) fix.
+   (merge 63ab08fb99 bc/run-command-nullness-after-free-fix later to maint).
+
+ * The code that tries to skip over the entries for the paths in a
+   single directory using the cache-tree was not careful enough
+   against corrupt index file.
+   (merge 573117dfa5 es/unpack-trees-oob-fix later to maint).
+
+ * Reduce unnecessary round-trip when running "ls-remote" over the
+   stateless RPC mechanism.
+   (merge 4d8cab95cc jk/no-flush-upon-disconnecting-slrpc-transport later to maint).
+
+ * "git restore --staged" did not correctly update the cache-tree
+   structure, resulting in bogus trees to be written afterwards, which
+   has been corrected.
+   (merge e701bab3e9 nd/switch-and-restore later to maint).
+
+ * The code recently added to move to the entry beyond the ones in the
+   same directory in the index in the sparse-cone mode did not count
+   the number of entries to skip over incorrectly, which has been
+   corrected.
+   (merge 7210ca4ee5 ds/sparse-cone later to maint).
+
+ * Rendering by "git log --graph" of ancestry lines leading to a merge
+   commit were made suboptimal to waste vertical space a bit with a
+   recent update, which has been corrected.
+   (merge c958d3bd0a ds/graph-horizontal-edges later to maint).
+
+ * Work around test breakages caused by custom regex engine used in
+   libasan, when address sanitizer is used with more recent versions
+   of gcc and clang.
+   (merge f65d07fffa jk/asan-build-fix later to maint).
+
+ * Minor bugfixes to "git add -i" that has recently been rewritten in C.
+   (merge 849e43cc18 js/builtin-add-i-cmds later to maint).
+
+ * "git fetch --refmap=" option has got a better documentation.
+   (merge b40a50264a ds/refmap-doc later to maint).
+
+ * "git checkout X" did not correctly fail when X is not a local
+   branch but could name more than one remote-tracking branches
+   (i.e. to be dwimmed as the starting point to create a corresponding
+   local branch), which has been corrected.
+   (merge fa74180d08 am/checkout-file-and-ref-ref-ambiguity later to maint).
+
+ * Corner case bugs in "git clean" that stems from a (necessarily for
+   performance reasons) awkward calling convention in the directory
+   enumeration API has been corrected.
+   (merge 0cbb60574e en/fill-directory-fixes-more later to maint).
+
+ * A fetch that is told to recursively fetch updates in submodules
+   inevitably produces reams of output, and it becomes hard to spot
+   error messages.  The command has been taught to enumerate
+   submodules that had errors at the end of the operation.
+   (merge 0222540827 es/fetch-show-failed-submodules-atend later to maint).
+
+ * The "--recurse-submodules" option of various subcommands did not
+   work well when run in an alternate worktree, which has been
+   corrected.
+   (merge a9472afb63 pb/recurse-submodule-in-worktree-fix later to maint).
+
+ * Other code cleanup, docfix, build fix, etc.
+   (merge 26f924d50e en/simplify-check-updates-in-unpack-trees later to maint).
+   (merge 065027ee1a en/string-list-can-be-custom-sorted later to maint).
+   (merge d0d0a357a1 am/update-pathspec-f-f-tests later to maint).
+   (merge f94f7bd00d am/test-pathspec-f-f-error-cases later to maint).
+   (merge e4837b4406 jk/test-fixes later to maint).
+   (merge a4ffbbbb99 rt/submodule-i18n later to maint).
+   (merge 856249c62a bc/actualmente later to maint).
+   (merge c513a958b6 ss/t6025-modernize later to maint).
+   (merge 69e104d70e bc/author-committer-doc later to maint).
+   (merge 7a2dc95cbc bc/misconception-doc later to maint).
+   (merge b441717256 dl/test-must-fail-fixes later to maint).
+   (merge d031049da3 mt/sparse-checkout-doc-update later to maint).
index cce2c8924598c240fcebfadea8be5b4b28ce378e..d94025cb3684d8e20f2875716cc16dfad47cf6ff 100644 (file)
@@ -18,3 +18,18 @@ gpg.<format>.program::
        chose. (see `gpg.program` and `gpg.format`) `gpg.program` can still
        be used as a legacy synonym for `gpg.openpgp.program`. The default
        value for `gpg.x509.program` is "gpgsm".
+
+gpg.minTrustLevel::
+       Specifies a minimum trust level for signature verification.  If
+       this option is unset, then signature verification for merge
+       operations require a key with at least `marginal` trust.  Other
+       operations that perform signature verification require a key
+       with at least `undefined` trust.  Setting this option overrides
+       the required trust-level for all operations.  Supported values,
+       in increasing order of significance:
++
+* `undefined`
+* `never`
+* `marginal`
+* `fully`
+* `ultimate`
index 5a32f5b0a5a9c312bb414d0405cab0f97e826a01..e806033aab86ae7efd52a26a1efd2067178c28dc 100644 (file)
@@ -71,7 +71,7 @@ http.saveCookies::
 http.version::
        Use the specified HTTP protocol version when communicating with a server.
        If you want to force the default. The available and default version depend
-       on libcurl. Actually the possible values of
+       on libcurl. Currently the possible values of
        this option are:
 
        - HTTP/2
@@ -84,7 +84,7 @@ http.sslVersion::
        particular configuration of the crypto library in use. Internally
        this sets the 'CURLOPT_SSL_VERSION' option; see the libcurl
        documentation for more details on the format of this option and
-       for the ssl version supported. Actually the possible values of
+       for the ssl version supported. Currently the possible values of
        this option are:
 
        - sslv2
@@ -199,6 +199,14 @@ http.postBuffer::
        Transfer-Encoding: chunked is used to avoid creating a
        massive pack file locally.  Default is 1 MiB, which is
        sufficient for most requests.
++
+Note that raising this limit is only effective for disabling chunked
+transfer encoding and therefore should be used only where the remote
+server or a proxy only supports HTTP/1.0 or is noncompliant with the
+HTTP standard.  Raising this is not, in general, an effective solution
+for most push problems, but can increase memory consumption
+significantly since the entire buffer is allocated even for small
+pushes.
 
 http.lowSpeedLimit, http.lowSpeedTime::
        If the HTTP transfer speed is less than 'http.lowSpeedLimit'
index 0557cbbceb8159148e8d0e7ab4771a8f53a67c0a..59aec7c3aed32aac0a8d7e015c0602705e4ccc6d 100644 (file)
@@ -13,7 +13,12 @@ committer.email::
        Also, all of these can be overridden by the `GIT_AUTHOR_NAME`,
        `GIT_AUTHOR_EMAIL`, `GIT_COMMITTER_NAME`,
        `GIT_COMMITTER_EMAIL` and `EMAIL` environment variables.
-       See linkgit:git-commit-tree[1] for more information.
++
+Note that the `name` forms of these variables conventionally refer to
+some form of a personal name.  See linkgit:git-commit[1] and the
+environment variables section of linkgit:git[1] for more information on
+these settings and the `credential.username` option if you're looking
+for authentication credentials instead.
 
 user.useConfigOnly::
        Instruct Git to avoid trying to guess defaults for `user.email`
index a2f78624a27e68dfe5a97adcac9cd67c109d1f52..a115a1ae0e973dc0375e82cbc1de5eff182aa9a1 100644 (file)
@@ -139,7 +139,10 @@ ifndef::git-pull[]
        specified refspec (can be given more than once) to map the
        refs to remote-tracking branches, instead of the values of
        `remote.*.fetch` configuration variables for the remote
-       repository.  See section on "Configured Remote-tracking
+       repository.  Providing an empty `<refspec>` to the
+       `--refmap` option causes Git to ignore the configured
+       refspecs and rely entirely on the refspecs supplied as
+       command-line arguments. See section on "Configured Remote-tracking
        Branches" for details.
 
 -t::
index 4b90b9c12a4a87fe563d367ab8252750021969d7..ec15ee8d6fad83d5dfe7d019cbb5984303552d9e 100644 (file)
@@ -69,7 +69,6 @@ OPTIONS
        Do not GPG-sign commit, to countermand a `--gpg-sign` option
        given earlier on the command line.
 
-
 Commit Information
 ------------------
 
@@ -79,26 +78,6 @@ A commit encapsulates:
 - author name, email and date
 - committer name and email and the commit time.
 
-While parent object ids are provided on the command line, author and
-committer information is taken from the following environment variables,
-if set:
-
-       GIT_AUTHOR_NAME
-       GIT_AUTHOR_EMAIL
-       GIT_AUTHOR_DATE
-       GIT_COMMITTER_NAME
-       GIT_COMMITTER_EMAIL
-       GIT_COMMITTER_DATE
-
-(nb "<", ">" and "\n"s are stripped)
-
-In case (some of) these environment variables are not set, the information
-is taken from the configuration items user.name and user.email, or, if not
-present, the environment variable EMAIL, or, if that is not set,
-system user name and the hostname used for outgoing mail (taken
-from `/etc/mailname` and falling back to the fully qualified hostname when
-that file does not exist).
-
 A commit comment is read from stdin. If a changelog
 entry is not provided via "<" redirection, 'git commit-tree' will just wait
 for one to be entered and terminated with ^D.
@@ -117,6 +96,7 @@ FILES
 SEE ALSO
 --------
 linkgit:git-write-tree[1]
+linkgit:git-commit[1]
 
 GIT
 ---
index ced5a9beab159de86148b94bc1825247ead79390..13f653989f32f1231030d8e89a70cb6a96c9955f 100644 (file)
@@ -367,9 +367,6 @@ changes to tracked files.
 +
 For more details, see the 'pathspec' entry in linkgit:gitglossary[7].
 
-:git-commit: 1
-include::date-formats.txt[]
-
 EXAMPLES
 --------
 When recording your own work, the contents of modified files in
@@ -463,6 +460,43 @@ alter the order the changes are committed, because the merge
 should be recorded as a single commit.  In fact, the command
 refuses to run when given pathnames (but see `-i` option).
 
+COMMIT INFORMATION
+------------------
+
+Author and committer information is taken from the following environment
+variables, if set:
+
+       GIT_AUTHOR_NAME
+       GIT_AUTHOR_EMAIL
+       GIT_AUTHOR_DATE
+       GIT_COMMITTER_NAME
+       GIT_COMMITTER_EMAIL
+       GIT_COMMITTER_DATE
+
+(nb "<", ">" and "\n"s are stripped)
+
+The author and committer names are by convention some form of a personal name
+(that is, the name by which other humans refer to you), although Git does not
+enforce or require any particular form. Arbitrary Unicode may be used, subject
+to the constraints listed above. This name has no effect on authentication; for
+that, see the `credential.username` variable in linkgit:git-config[1].
+
+In case (some of) these environment variables are not set, the information
+is taken from the configuration items `user.name` and `user.email`, or, if not
+present, the environment variable EMAIL, or, if that is not set,
+system user name and the hostname used for outgoing mail (taken
+from `/etc/mailname` and falling back to the fully qualified hostname when
+that file does not exist).
+
+The `author.name` and `committer.name` and their corresponding email options
+override `user.name` and `user.email` if set and are overridden themselves by
+the environment variables.
+
+The typical usage is to set just the `user.name` and `user.email` variables;
+the other options are provided for more complex use cases.
+
+:git-commit: 1
+include::date-formats.txt[]
 
 DISCUSSION
 ----------
index 974ade22387f23905938e2a898f730168f7dd76b..e6f235a0c485a550eb1232e302304efdb8de81ac 100644 (file)
@@ -41,6 +41,10 @@ COMMANDS
 To avoid interfering with other worktrees, it first enables the
 `extensions.worktreeConfig` setting and makes sure to set the
 `core.sparseCheckout` setting in the worktree-specific config file.
++
+When `--cone` is provided, the `core.sparseCheckoutCone` setting is
+also set, allowing for better performance with a limited set of
+patterns (see 'CONE PATTERN SET' below).
 
 'set'::
        Write a set of patterns to the sparse-checkout file, as given as
index c7a6271daf61507459fe3bb1a0bf494def4f994f..1489cb09a09997a64a102b58a8bf2b890640d6df 100644 (file)
@@ -549,6 +549,22 @@ The untracked cache extension can be enabled by the
 `core.untrackedCache` configuration variable (see
 linkgit:git-config[1]).
 
+NOTES
+-----
+
+Users often try to use the assume-unchanged and skip-worktree bits
+to tell Git to ignore changes to files that are tracked.  This does not
+work as expected, since Git may still check working tree files against
+the index when performing certain operations.  In general, Git does not
+provide a way to ignore changes to tracked files, so alternate solutions
+are recommended.
+
+For example, if the file you want to change is some sort of config file,
+the repository can include a sample config file that can then be copied
+into the ignored name and modified.  The repository can even include a
+script to treat the sample file as a template, modifying and copying it
+automatically.
+
 SEE ALSO
 --------
 linkgit:git-config[1],
index b1597ac002f15492b861b332519cf8b5ea2d9228..0093c647bf64bc5a42d32b948e2d342c0ce61a49 100644 (file)
@@ -482,13 +482,36 @@ double-quotes and respecting backslash escapes. E.g., the value
 Git Commits
 ~~~~~~~~~~~
 `GIT_AUTHOR_NAME`::
+       The human-readable name used in the author identity when creating commit or
+       tag objects, or when writing reflogs. Overrides the `user.name` and
+       `author.name` configuration settings.
+
 `GIT_AUTHOR_EMAIL`::
+       The email address used in the author identity when creating commit or
+       tag objects, or when writing reflogs. Overrides the `user.email` and
+       `author.email` configuration settings.
+
 `GIT_AUTHOR_DATE`::
+       The date used for the author identity when creating commit or tag objects, or
+       when writing reflogs. See linkgit:git-commit[1] for valid formats.
+
 `GIT_COMMITTER_NAME`::
+       The human-readable name used in the committer identity when creating commit or
+       tag objects, or when writing reflogs. Overrides the `user.name` and
+       `committer.name` configuration settings.
+
 `GIT_COMMITTER_EMAIL`::
+       The email address used in the author identity when creating commit or
+       tag objects, or when writing reflogs. Overrides the `user.email` and
+       `committer.email` configuration settings.
+
 `GIT_COMMITTER_DATE`::
-'EMAIL'::
-       see linkgit:git-commit-tree[1]
+       The date used for the committer identity when creating commit or tag objects, or
+       when writing reflogs. See linkgit:git-commit[1] for valid formats.
+
+`EMAIL`::
+       The email address used in the author and committer identities if no other
+       relevant environment variable or configuration setting has been set.
 
 Git Diffs
 ~~~~~~~~~
index f880d21dfb520c4a7bed90ccd0f2cc9d3961ecf7..c0b95256cc8c8d640d284f77b23d63c8785601d1 100644 (file)
@@ -751,7 +751,7 @@ to it.
 ================================================
 If you make the decision to start your new branch at some
 other point in the history than the current `HEAD`, you can do so by
-just telling 'git checkout' what the base of the checkout would be.
+just telling 'git switch' what the base of the checkout would be.
 In other words, if you have an earlier tag or branch, you'd just do
 
 ------------
index 1a7212ce5ab8147a810c10395bb4b558958cf6ac..a4b6f49186319ae094d3a7182ab9f7e0a8925ef9 100644 (file)
@@ -226,6 +226,7 @@ endif::git-rev-list[]
 '%GF':: show the fingerprint of the key used to sign a signed commit
 '%GP':: show the fingerprint of the primary key whose subkey was used
        to sign a signed commit
+'%GT':: show the trust level for the key used to sign a signed commit
 '%gD':: reflog selector, e.g., `refs/stash@{1}` or `refs/stash@{2
        minutes ago}`; the format follows the rules described for the
        `-g` option. The portion before the `@` is the refname as
index 2b7f62b8bfa042f10c5a0a76bdf33e002e7ff586..616d5a6404e4f1a6550b2aca89488889dbe6d34f 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.25.0
+DEF_VER=v2.25.GIT
 
 LF='
 '
index 09f98b777cae1dc9226f13c182011adacfd8b2fc..6134104ae65b713b665a40933b2df1a8c58e2269 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -721,6 +721,7 @@ TEST_BUILTINS_OBJS += test-mktemp.o
 TEST_BUILTINS_OBJS += test-oidmap.o
 TEST_BUILTINS_OBJS += test-online-cpus.o
 TEST_BUILTINS_OBJS += test-parse-options.o
+TEST_BUILTINS_OBJS += test-parse-pathspec-file.o
 TEST_BUILTINS_OBJS += test-path-utils.o
 TEST_BUILTINS_OBJS += test-pkt-line.o
 TEST_BUILTINS_OBJS += test-prio-queue.o
@@ -1220,6 +1221,9 @@ endif
 ifneq ($(filter leak,$(SANITIZERS)),)
 BASIC_CFLAGS += -DSUPPRESS_ANNOTATED_LEAKS
 endif
+ifneq ($(filter address,$(SANITIZERS)),)
+NO_REGEX = NeededForASAN
+endif
 endif
 
 ifndef sysconfdir
index 091dd024b349d6bc908371eddb7c594059c4fd70..370269d8dfc14c9dda695f8bdbc84febe64771a3 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.25.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.26.0.txt
\ No newline at end of file
index 6a5048c83e4d6f08ae3ecfdb6beab125cde10a05..4a9bf85cac033b0cf22665918fccee561678eb3d 100644 (file)
@@ -52,6 +52,24 @@ void init_add_i_state(struct add_i_state *s, struct repository *r)
                diff_get_color(s->use_color, DIFF_FILE_OLD));
        init_color(r, s, "new", s->file_new_color,
                diff_get_color(s->use_color, DIFF_FILE_NEW));
+
+       FREE_AND_NULL(s->interactive_diff_filter);
+       git_config_get_string("interactive.difffilter",
+                             &s->interactive_diff_filter);
+
+       FREE_AND_NULL(s->interactive_diff_algorithm);
+       git_config_get_string("diff.algorithm",
+                             &s->interactive_diff_algorithm);
+
+       git_config_get_bool("interactive.singlekey", &s->use_single_key);
+}
+
+void clear_add_i_state(struct add_i_state *s)
+{
+       FREE_AND_NULL(s->interactive_diff_filter);
+       FREE_AND_NULL(s->interactive_diff_algorithm);
+       memset(s, 0, sizeof(*s));
+       s->use_color = -1;
 }
 
 /*
@@ -326,7 +344,10 @@ static ssize_t list_and_choose(struct add_i_state *s,
                                if (endp == p + sep)
                                        to = from + 1;
                                else if (*endp == '-') {
-                                       to = strtoul(++endp, &endp, 10);
+                                       if (isdigit(*(++endp)))
+                                               to = strtoul(endp, &endp, 10);
+                                       else
+                                               to = items->items.nr;
                                        /* extra characters after the range? */
                                        if (endp != p + sep)
                                                from = -1;
@@ -913,7 +934,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
 
        opts->prompt = N_("Patch update");
        count = list_and_choose(s, files, opts);
-       if (count >= 0) {
+       if (count > 0) {
                struct argv_array args = ARGV_ARRAY_INIT;
                struct pathspec ps_selected = { 0 };
 
@@ -924,7 +945,7 @@ static int run_patch(struct add_i_state *s, const struct pathspec *ps,
                parse_pathspec(&ps_selected,
                               PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
                               PATHSPEC_LITERAL_PATH, "", args.argv);
-               res = run_add_p(s->r, &ps_selected);
+               res = run_add_p(s->r, ADD_P_ADD, NULL, &ps_selected);
                argv_array_clear(&args);
                clear_pathspec(&ps_selected);
        }
@@ -954,7 +975,7 @@ static int run_diff(struct add_i_state *s, const struct pathspec *ps,
        opts->flags = IMMEDIATE;
        count = list_and_choose(s, files, opts);
        opts->flags = 0;
-       if (count >= 0) {
+       if (count > 0) {
                struct argv_array args = ARGV_ARRAY_INIT;
 
                argv_array_pushl(&args, "git", "diff", "-p", "--cached",
@@ -1149,6 +1170,7 @@ int run_add_i(struct repository *r, const struct pathspec *ps)
        strbuf_release(&print_file_item_data.worktree);
        strbuf_release(&header);
        prefix_item_list_clear(&commands);
+       clear_add_i_state(&s);
 
        return res;
 }
index 062dc3646c2fd41df3b767d8fa64c8e454e846c0..693f125e8e4bc64ac0ccc8ac3b8dea812a397330 100644 (file)
@@ -15,13 +15,27 @@ struct add_i_state {
        char context_color[COLOR_MAXLEN];
        char file_old_color[COLOR_MAXLEN];
        char file_new_color[COLOR_MAXLEN];
+
+       int use_single_key;
+       char *interactive_diff_filter, *interactive_diff_algorithm;
 };
 
 void init_add_i_state(struct add_i_state *s, struct repository *r);
+void clear_add_i_state(struct add_i_state *s);
 
 struct repository;
 struct pathspec;
 int run_add_i(struct repository *r, const struct pathspec *ps);
-int run_add_p(struct repository *r, const struct pathspec *ps);
+
+enum add_p_mode {
+       ADD_P_ADD,
+       ADD_P_STASH,
+       ADD_P_RESET,
+       ADD_P_CHECKOUT,
+       ADD_P_WORKTREE,
+};
+
+int run_add_p(struct repository *r, enum add_p_mode mode,
+             const char *revision, const struct pathspec *ps);
 
 #endif
index 2c46fe5b3332bf844007ea0d17dee250f0ba5756..d8dafa8168dc8389468d8c2c3cd6221220826605 100644 (file)
 #include "pathspec.h"
 #include "color.h"
 #include "diff.h"
+#include "compat/terminal.h"
 
 enum prompt_mode_type {
-       PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK
+       PROMPT_MODE_CHANGE = 0, PROMPT_DELETION, PROMPT_HUNK,
+       PROMPT_MODE_MAX, /* must be last */
 };
 
-static const char *prompt_mode[] = {
-       N_("Stage mode change [y,n,a,q,d%s,?]? "),
-       N_("Stage deletion [y,n,a,q,d%s,?]? "),
-       N_("Stage this hunk [y,n,a,q,d%s,?]? ")
+struct patch_mode {
+       /*
+        * The magic constant 4 is chosen such that all patch modes
+        * provide enough space for three command-line arguments followed by a
+        * trailing `NULL`.
+        */
+       const char *diff_cmd[4], *apply_args[4], *apply_check_args[4];
+       unsigned is_reverse:1, index_only:1, apply_for_checkout:1;
+       const char *prompt_mode[PROMPT_MODE_MAX];
+       const char *edit_hunk_hint, *help_patch_text;
+};
+
+static struct patch_mode patch_mode_add = {
+       .diff_cmd = { "diff-files", NULL },
+       .apply_args = { "--cached", NULL },
+       .apply_check_args = { "--cached", NULL },
+       .prompt_mode = {
+               N_("Stage mode change [y,n,q,a,d%s,?]? "),
+               N_("Stage deletion [y,n,q,a,d%s,?]? "),
+               N_("Stage this hunk [y,n,q,a,d%s,?]? ")
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for staging."),
+       .help_patch_text =
+               N_("y - stage this hunk\n"
+                  "n - do not stage this hunk\n"
+                  "q - quit; do not stage this hunk or any of the remaining "
+                       "ones\n"
+                  "a - stage this hunk and all later hunks in the file\n"
+                  "d - do not stage this hunk or any of the later hunks in "
+                       "the file\n")
+};
+
+static struct patch_mode patch_mode_stash = {
+       .diff_cmd = { "diff-index", "HEAD", NULL },
+       .apply_args = { "--cached", NULL },
+       .apply_check_args = { "--cached", NULL },
+       .prompt_mode = {
+               N_("Stash mode change [y,n,q,a,d%s,?]? "),
+               N_("Stash deletion [y,n,q,a,d%s,?]? "),
+               N_("Stash this hunk [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for stashing."),
+       .help_patch_text =
+               N_("y - stash this hunk\n"
+                  "n - do not stash this hunk\n"
+                  "q - quit; do not stash this hunk or any of the remaining "
+                       "ones\n"
+                  "a - stash this hunk and all later hunks in the file\n"
+                  "d - do not stash this hunk or any of the later hunks in "
+                       "the file\n"),
+};
+
+static struct patch_mode patch_mode_reset_head = {
+       .diff_cmd = { "diff-index", "--cached", NULL },
+       .apply_args = { "-R", "--cached", NULL },
+       .apply_check_args = { "-R", "--cached", NULL },
+       .is_reverse = 1,
+       .index_only = 1,
+       .prompt_mode = {
+               N_("Unstage mode change [y,n,q,a,d%s,?]? "),
+               N_("Unstage deletion [y,n,q,a,d%s,?]? "),
+               N_("Unstage this hunk [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for unstaging."),
+       .help_patch_text =
+               N_("y - unstage this hunk\n"
+                  "n - do not unstage this hunk\n"
+                  "q - quit; do not unstage this hunk or any of the remaining "
+                       "ones\n"
+                  "a - unstage this hunk and all later hunks in the file\n"
+                  "d - do not unstage this hunk or any of the later hunks in "
+                       "the file\n"),
+};
+
+static struct patch_mode patch_mode_reset_nothead = {
+       .diff_cmd = { "diff-index", "-R", "--cached", NULL },
+       .apply_args = { "--cached", NULL },
+       .apply_check_args = { "--cached", NULL },
+       .index_only = 1,
+       .prompt_mode = {
+               N_("Apply mode change to index [y,n,q,a,d%s,?]? "),
+               N_("Apply deletion to index [y,n,q,a,d%s,?]? "),
+               N_("Apply this hunk to index [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for applying."),
+       .help_patch_text =
+               N_("y - apply this hunk to index\n"
+                  "n - do not apply this hunk to index\n"
+                  "q - quit; do not apply this hunk or any of the remaining "
+                       "ones\n"
+                  "a - apply this hunk and all later hunks in the file\n"
+                  "d - do not apply this hunk or any of the later hunks in "
+                       "the file\n"),
+};
+
+static struct patch_mode patch_mode_checkout_index = {
+       .diff_cmd = { "diff-files", NULL },
+       .apply_args = { "-R", NULL },
+       .apply_check_args = { "-R", NULL },
+       .is_reverse = 1,
+       .prompt_mode = {
+               N_("Discard mode change from worktree [y,n,q,a,d%s,?]? "),
+               N_("Discard deletion from worktree [y,n,q,a,d%s,?]? "),
+               N_("Discard this hunk from worktree [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for discarding."),
+       .help_patch_text =
+               N_("y - discard this hunk from worktree\n"
+                  "n - do not discard this hunk from worktree\n"
+                  "q - quit; do not discard this hunk or any of the remaining "
+                       "ones\n"
+                  "a - discard this hunk and all later hunks in the file\n"
+                  "d - do not discard this hunk or any of the later hunks in "
+                       "the file\n"),
+};
+
+static struct patch_mode patch_mode_checkout_head = {
+       .diff_cmd = { "diff-index", NULL },
+       .apply_for_checkout = 1,
+       .apply_check_args = { "-R", NULL },
+       .is_reverse = 1,
+       .prompt_mode = {
+               N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for discarding."),
+       .help_patch_text =
+               N_("y - discard this hunk from index and worktree\n"
+                  "n - do not discard this hunk from index and worktree\n"
+                  "q - quit; do not discard this hunk or any of the remaining "
+                       "ones\n"
+                  "a - discard this hunk and all later hunks in the file\n"
+                  "d - do not discard this hunk or any of the later hunks in "
+                       "the file\n"),
+};
+
+static struct patch_mode patch_mode_checkout_nothead = {
+       .diff_cmd = { "diff-index", "-R", NULL },
+       .apply_for_checkout = 1,
+       .apply_check_args = { NULL },
+       .prompt_mode = {
+               N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for applying."),
+       .help_patch_text =
+               N_("y - apply this hunk to index and worktree\n"
+                  "n - do not apply this hunk to index and worktree\n"
+                  "q - quit; do not apply this hunk or any of the remaining "
+                       "ones\n"
+                  "a - apply this hunk and all later hunks in the file\n"
+                  "d - do not apply this hunk or any of the later hunks in "
+                       "the file\n"),
+};
+
+static struct patch_mode patch_mode_worktree_head = {
+       .diff_cmd = { "diff-index", NULL },
+       .apply_args = { "-R", NULL },
+       .apply_check_args = { "-R", NULL },
+       .is_reverse = 1,
+       .prompt_mode = {
+               N_("Discard mode change from index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Discard deletion from index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Discard this hunk from index and worktree [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for discarding."),
+       .help_patch_text =
+               N_("y - discard this hunk from worktree\n"
+                  "n - do not discard this hunk from worktree\n"
+                  "q - quit; do not discard this hunk or any of the remaining "
+                       "ones\n"
+                  "a - discard this hunk and all later hunks in the file\n"
+                  "d - do not discard this hunk or any of the later hunks in "
+                       "the file\n"),
+};
+
+static struct patch_mode patch_mode_worktree_nothead = {
+       .diff_cmd = { "diff-index", "-R", NULL },
+       .apply_args = { NULL },
+       .apply_check_args = { NULL },
+       .prompt_mode = {
+               N_("Apply mode change to index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Apply deletion to index and worktree [y,n,q,a,d%s,?]? "),
+               N_("Apply this hunk to index and worktree [y,n,q,a,d%s,?]? "),
+       },
+       .edit_hunk_hint = N_("If the patch applies cleanly, the edited hunk "
+                            "will immediately be marked for applying."),
+       .help_patch_text =
+               N_("y - apply this hunk to worktree\n"
+                  "n - do not apply this hunk to worktree\n"
+                  "q - quit; do not apply this hunk or any of the remaining "
+                       "ones\n"
+                  "a - apply this hunk and all later hunks in the file\n"
+                  "d - do not apply this hunk or any of the later hunks in "
+                       "the file\n"),
 };
 
 struct hunk_header {
@@ -47,6 +250,10 @@ struct add_p_state {
                unsigned deleted:1, mode_change:1,binary:1;
        } *file_diff;
        size_t file_diff_nr;
+
+       /* patch mode */
+       struct patch_mode *mode;
+       const char *revision;
 };
 
 static void err(struct add_p_state *s, const char *fmt, ...)
@@ -154,6 +361,7 @@ static int is_octal(const char *p, size_t len)
 static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
 {
        struct argv_array args = ARGV_ARRAY_INIT;
+       const char *diff_algorithm = s->s.interactive_diff_algorithm;
        struct strbuf *plain = &s->plain, *colored = NULL;
        struct child_process cp = CHILD_PROCESS_INIT;
        char *p, *pend, *colored_p = NULL, *colored_pend = NULL, marker = '\0';
@@ -162,9 +370,20 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
        struct hunk *hunk = NULL;
        int res;
 
+       argv_array_pushv(&args, s->mode->diff_cmd);
+       if (diff_algorithm)
+               argv_array_pushf(&args, "--diff-algorithm=%s", diff_algorithm);
+       if (s->revision) {
+               struct object_id oid;
+               argv_array_push(&args,
+                               /* could be on an unborn branch */
+                               !strcmp("HEAD", s->revision) &&
+                               get_oid("HEAD", &oid) ?
+                               empty_tree_oid_hex() : s->revision);
+       }
+       color_arg_index = args.argc;
        /* Use `--no-color` explicitly, just in case `diff.color = always`. */
-       argv_array_pushl(&args, "diff-files", "-p", "--no-color", "--", NULL);
-       color_arg_index = args.argc - 2;
+       argv_array_pushl(&args, "--no-color", "-p", "--", NULL);
        for (i = 0; i < ps->nr; i++)
                argv_array_push(&args, ps->items[i].original);
 
@@ -183,6 +402,7 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
 
        if (want_color_fd(1, -1)) {
                struct child_process colored_cp = CHILD_PROCESS_INIT;
+               const char *diff_filter = s->s.interactive_diff_filter;
 
                setup_child_process(s, &colored_cp, NULL);
                xsnprintf((char *)args.argv[color_arg_index], 8, "--color");
@@ -192,6 +412,24 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                argv_array_clear(&args);
                if (res)
                        return error(_("could not parse colored diff"));
+
+               if (diff_filter) {
+                       struct child_process filter_cp = CHILD_PROCESS_INIT;
+
+                       setup_child_process(s, &filter_cp,
+                                           diff_filter, NULL);
+                       filter_cp.git_cmd = 0;
+                       filter_cp.use_shell = 1;
+                       strbuf_reset(&s->buf);
+                       if (pipe_command(&filter_cp,
+                                        colored->buf, colored->len,
+                                        &s->buf, colored->len,
+                                        NULL, 0) < 0)
+                               return error(_("failed to run '%s'"),
+                                            diff_filter);
+                       strbuf_swap(colored, &s->buf);
+               }
+
                strbuf_complete_line(colored);
                colored_p = colored->buf;
                colored_pend = colored_p + colored->len;
@@ -316,6 +554,9 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                                                   colored_pend - colored_p);
                        if (colored_eol)
                                colored_p = colored_eol + 1;
+                       else if (p != pend)
+                               /* colored shorter than non-colored? */
+                               goto mismatched_output;
                        else
                                colored_p = colored_pend;
 
@@ -340,6 +581,15 @@ static int parse_diff(struct add_p_state *s, const struct pathspec *ps)
                 */
                hunk->splittable_into++;
 
+       /* non-colored shorter than colored? */
+       if (colored_p != colored_pend) {
+mismatched_output:
+               error(_("mismatched output from interactive.diffFilter"));
+               advise(_("Your filter must maintain a one-to-one correspondence\n"
+                        "between its input and output lines."));
+               return -1;
+       }
+
        return 0;
 }
 
@@ -382,7 +632,10 @@ static void render_hunk(struct add_p_state *s, struct hunk *hunk,
                                - header->colored_extra_start;
                }
 
-               new_offset += delta;
+               if (s->mode->is_reverse)
+                       old_offset -= delta;
+               else
+                       new_offset += delta;
 
                strbuf_addf(out, "@@ -%lu,%lu +%lu,%lu @@",
                            old_offset, header->old_count,
@@ -805,11 +1058,10 @@ static int edit_hunk_manually(struct add_p_state *s, struct hunk *hunk)
                                "(context).\n"
                                "To remove '%c' lines, delete them.\n"
                                "Lines starting with %c will be removed.\n"),
-                             '-', '+', comment_line_char);
-       strbuf_commented_addf(&s->buf,
-                             _("If the patch applies cleanly, the edited hunk "
-                               "will immediately be\n"
-                               "marked for staging.\n"));
+                             s->mode->is_reverse ? '+' : '-',
+                             s->mode->is_reverse ? '-' : '+',
+                             comment_line_char);
+       strbuf_commented_addf(&s->buf, "%s", _(s->mode->edit_hunk_hint));
        /*
         * TRANSLATORS: 'it' refers to the patch mentioned in the previous
         * messages.
@@ -890,21 +1142,35 @@ static int run_apply_check(struct add_p_state *s,
        reassemble_patch(s, file_diff, 1, &s->buf);
 
        setup_child_process(s, &cp,
-                           "apply", "--cached", "--check", NULL);
+                           "apply", "--check", NULL);
+       argv_array_pushv(&cp.args, s->mode->apply_check_args);
        if (pipe_command(&cp, s->buf.buf, s->buf.len, NULL, 0, NULL, 0))
                return error(_("'git apply --cached' failed"));
 
        return 0;
 }
 
+static int read_single_character(struct add_p_state *s)
+{
+       if (s->s.use_single_key) {
+               int res = read_key_without_echo(&s->answer);
+               printf("%s\n", res == EOF ? "" : s->answer.buf);
+               return res;
+       }
+
+       if (strbuf_getline(&s->answer, stdin) == EOF)
+               return EOF;
+       strbuf_trim_trailing_newline(&s->answer);
+       return 0;
+}
+
 static int prompt_yesno(struct add_p_state *s, const char *prompt)
 {
        for (;;) {
                color_fprintf(stdout, s->s.prompt_color, "%s", _(prompt));
                fflush(stdout);
-               if (strbuf_getline(&s->answer, stdin) == EOF)
+               if (read_single_character(s) == EOF)
                        return -1;
-               strbuf_trim_trailing_newline(&s->answer);
                switch (tolower(s->answer.buf[0])) {
                case 'n': return 0;
                case 'y': return 1;
@@ -957,6 +1223,57 @@ static int edit_hunk_loop(struct add_p_state *s,
        }
 }
 
+static int apply_for_checkout(struct add_p_state *s, struct strbuf *diff,
+                             int is_reverse)
+{
+       const char *reverse = is_reverse ? "-R" : NULL;
+       struct child_process check_index = CHILD_PROCESS_INIT;
+       struct child_process check_worktree = CHILD_PROCESS_INIT;
+       struct child_process apply_index = CHILD_PROCESS_INIT;
+       struct child_process apply_worktree = CHILD_PROCESS_INIT;
+       int applies_index, applies_worktree;
+
+       setup_child_process(s, &check_index,
+                           "apply", "--cached", "--check", reverse, NULL);
+       applies_index = !pipe_command(&check_index, diff->buf, diff->len,
+                                     NULL, 0, NULL, 0);
+
+       setup_child_process(s, &check_worktree,
+                           "apply", "--check", reverse, NULL);
+       applies_worktree = !pipe_command(&check_worktree, diff->buf, diff->len,
+                                        NULL, 0, NULL, 0);
+
+       if (applies_worktree && applies_index) {
+               setup_child_process(s, &apply_index,
+                                   "apply", "--cached", reverse, NULL);
+               pipe_command(&apply_index, diff->buf, diff->len,
+                            NULL, 0, NULL, 0);
+
+               setup_child_process(s, &apply_worktree,
+                                   "apply", reverse, NULL);
+               pipe_command(&apply_worktree, diff->buf, diff->len,
+                            NULL, 0, NULL, 0);
+
+               return 1;
+       }
+
+       if (!applies_index) {
+               err(s, _("The selected hunks do not apply to the index!"));
+               if (prompt_yesno(s, _("Apply them to the worktree "
+                                         "anyway? ")) > 0) {
+                       setup_child_process(s, &apply_worktree,
+                                           "apply", reverse, NULL);
+                       return pipe_command(&apply_worktree, diff->buf,
+                                           diff->len, NULL, 0, NULL, 0);
+               }
+               err(s, _("Nothing was applied.\n"));
+       } else
+               /* As a last resort, show the diff to the user */
+               fwrite(diff->buf, diff->len, 1, stderr);
+
+       return 0;
+}
+
 #define SUMMARY_HEADER_WIDTH 20
 #define SUMMARY_LINE_WIDTH 80
 static void summarize_hunk(struct add_p_state *s, struct hunk *hunk,
@@ -1005,13 +1322,6 @@ static size_t display_hunks(struct add_p_state *s,
        return end_index;
 }
 
-static const char help_patch_text[] =
-N_("y - stage this hunk\n"
-   "n - do not stage this hunk\n"
-   "q - quit; do not stage this hunk or any of the remaining ones\n"
-   "a - stage this and all the remaining hunks\n"
-   "d - do not stage this hunk nor any of the remaining hunks\n");
-
 static const char help_patch_remainder[] =
 N_("j - leave this hunk undecided, see next undecided hunk\n"
    "J - leave this hunk undecided, see next hunk\n"
@@ -1097,11 +1407,11 @@ static int patch_update_file(struct add_p_state *s,
                              (uintmax_t)hunk_index + 1,
                              (uintmax_t)file_diff->hunk_nr);
                color_fprintf(stdout, s->s.prompt_color,
-                             _(prompt_mode[prompt_mode_type]), s->buf.buf);
+                             _(s->mode->prompt_mode[prompt_mode_type]),
+                             s->buf.buf);
                fflush(stdout);
-               if (strbuf_getline(&s->answer, stdin) == EOF)
+               if (read_single_character(s) == EOF)
                        break;
-               strbuf_trim_trailing_newline(&s->answer);
 
                if (!s->answer.len)
                        continue;
@@ -1254,7 +1564,7 @@ soft_increment:
                        const char *p = _(help_patch_remainder), *eol = p;
 
                        color_fprintf(stdout, s->s.help_color, "%s",
-                                     _(help_patch_text));
+                                     _(s->mode->help_patch_text));
 
                        /*
                         * Show only those lines of the remainder that are
@@ -1288,10 +1598,16 @@ soft_increment:
                reassemble_patch(s, file_diff, 0, &s->buf);
 
                discard_index(s->s.r->index);
-               setup_child_process(s, &cp, "apply", "--cached", NULL);
-               if (pipe_command(&cp, s->buf.buf, s->buf.len,
-                                NULL, 0, NULL, 0))
-                       error(_("'git apply --cached' failed"));
+               if (s->mode->apply_for_checkout)
+                       apply_for_checkout(s, &s->buf,
+                                          s->mode->is_reverse);
+               else {
+                       setup_child_process(s, &cp, "apply", NULL);
+                       argv_array_pushv(&cp.args, s->mode->apply_args);
+                       if (pipe_command(&cp, s->buf.buf, s->buf.len,
+                                        NULL, 0, NULL, 0))
+                               error(_("'git apply' failed"));
+               }
                if (!repo_read_index(s->s.r))
                        repo_refresh_and_write_index(s->s.r, REFRESH_QUIET, 0,
                                                     1, NULL, NULL, NULL);
@@ -1301,7 +1617,8 @@ soft_increment:
        return quit;
 }
 
-int run_add_p(struct repository *r, const struct pathspec *ps)
+int run_add_p(struct repository *r, enum add_p_mode mode,
+             const char *revision, const struct pathspec *ps)
 {
        struct add_p_state s = {
                { r }, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT, STRBUF_INIT
@@ -1310,12 +1627,39 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
 
        init_add_i_state(&s.s, r);
 
+       if (mode == ADD_P_STASH)
+               s.mode = &patch_mode_stash;
+       else if (mode == ADD_P_RESET) {
+               if (!revision || !strcmp(revision, "HEAD"))
+                       s.mode = &patch_mode_reset_head;
+               else
+                       s.mode = &patch_mode_reset_nothead;
+       } else if (mode == ADD_P_CHECKOUT) {
+               if (!revision)
+                       s.mode = &patch_mode_checkout_index;
+               else if (!strcmp(revision, "HEAD"))
+                       s.mode = &patch_mode_checkout_head;
+               else
+                       s.mode = &patch_mode_checkout_nothead;
+       } else if (mode == ADD_P_WORKTREE) {
+               if (!revision)
+                       s.mode = &patch_mode_checkout_index;
+               else if (!strcmp(revision, "HEAD"))
+                       s.mode = &patch_mode_worktree_head;
+               else
+                       s.mode = &patch_mode_worktree_nothead;
+       } else
+               s.mode = &patch_mode_add;
+       s.revision = revision;
+
        if (discard_index(r->index) < 0 || repo_read_index(r) < 0 ||
-           repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
-                                        NULL, NULL, NULL) < 0 ||
+           (!s.mode->index_only &&
+            repo_refresh_and_write_index(r, REFRESH_QUIET, 0, 1,
+                                         NULL, NULL, NULL) < 0) ||
            parse_diff(&s, ps) < 0) {
                strbuf_release(&s.plain);
                strbuf_release(&s.colored);
+               clear_add_i_state(&s.s);
                return -1;
        }
 
@@ -1334,5 +1678,6 @@ int run_add_p(struct repository *r, const struct pathspec *ps)
        strbuf_release(&s.buf);
        strbuf_release(&s.plain);
        strbuf_release(&s.colored);
+       clear_add_i_state(&s.s);
        return 0;
 }
index 4c38aff41957a07cc52eee8ac64bbb4afe2969e4..f7e627f3b9ba80ada06e988d89862996e5e0e72b 100644 (file)
@@ -31,6 +31,7 @@ static int take_worktree_changes;
 static int add_renormalize;
 static int pathspec_file_nul;
 static const char *pathspec_from_file;
+static int legacy_stash_p; /* support for the scripted `git stash` */
 
 struct update_callback_data {
        int flags;
@@ -196,12 +197,25 @@ int run_add_interactive(const char *revision, const char *patch_mode,
                                    &use_builtin_add_i);
 
        if (use_builtin_add_i == 1) {
+               enum add_p_mode mode;
+
                if (!patch_mode)
                        return !!run_add_i(the_repository, pathspec);
-               if (strcmp(patch_mode, "--patch"))
-                       die("'%s' not yet supported in the built-in add -p",
-                           patch_mode);
-               return !!run_add_p(the_repository, pathspec);
+
+               if (!strcmp(patch_mode, "--patch"))
+                       mode = ADD_P_ADD;
+               else if (!strcmp(patch_mode, "--patch=stash"))
+                       mode = ADD_P_STASH;
+               else if (!strcmp(patch_mode, "--patch=reset"))
+                       mode = ADD_P_RESET;
+               else if (!strcmp(patch_mode, "--patch=checkout"))
+                       mode = ADD_P_CHECKOUT;
+               else if (!strcmp(patch_mode, "--patch=worktree"))
+                       mode = ADD_P_WORKTREE;
+               else
+                       die("'%s' not supported", patch_mode);
+
+               return !!run_add_p(the_repository, mode, revision, pathspec);
        }
 
        argv_array_push(&argv, "add--interactive");
@@ -327,6 +341,8 @@ static struct option builtin_add_options[] = {
                   N_("override the executable bit of the listed files")),
        OPT_HIDDEN_BOOL(0, "warn-embedded-repo", &warn_on_embedded_repo,
                        N_("warn when adding an embedded repository")),
+       OPT_HIDDEN_BOOL(0, "legacy-stash-p", &legacy_stash_p,
+                       N_("backend for `git stash -p`")),
        OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
        OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
        OPT_END(),
@@ -428,6 +444,17 @@ int cmd_add(int argc, const char **argv, const char *prefix)
                        die(_("--pathspec-from-file is incompatible with --interactive/--patch"));
                exit(interactive_add(argc - 1, argv + 1, prefix, patch_interactive));
        }
+       if (legacy_stash_p) {
+               struct pathspec pathspec;
+
+               parse_pathspec(&pathspec, 0,
+                       PATHSPEC_PREFER_FULL |
+                       PATHSPEC_SYMLINK_LEADING_PATH |
+                       PATHSPEC_PREFIX_ORIGIN,
+                       prefix, argv);
+
+               return run_add_interactive(NULL, "--patch=stash", &pathspec);
+       }
 
        if (edit_interactive) {
                if (pathspec_from_file)
index b52c490c8f5404e24c05db289fc22539faf276e3..fc2eb1befcccecdc3bd583941d46a9a6e23c9496 100644 (file)
@@ -524,6 +524,8 @@ static int checkout_paths(const struct checkout_opts *opts,
        /* Now we are committed to check them out */
        if (opts->checkout_worktree)
                errs |= checkout_worktree(opts);
+       else
+               remove_marked_cache_entries(&the_index, 1);
 
        /*
         * Allow updating the index when checking out from the index.
@@ -1115,12 +1117,43 @@ static void setup_new_branch_info_and_source_tree(
        }
 }
 
+static const char *parse_remote_branch(const char *arg,
+                                      struct object_id *rev,
+                                      int could_be_checkout_paths)
+{
+       int num_matches = 0;
+       const char *remote = unique_tracking_name(arg, rev, &num_matches);
+
+       if (remote && could_be_checkout_paths) {
+               die(_("'%s' could be both a local file and a tracking branch.\n"
+                       "Please use -- (and optionally --no-guess) to disambiguate"),
+                   arg);
+       }
+
+       if (!remote && num_matches > 1) {
+           if (advice_checkout_ambiguous_remote_branch_name) {
+                   advise(_("If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
+                            "you can do so by fully qualifying the name with the --track option:\n"
+                            "\n"
+                            "    git checkout --track origin/<name>\n"
+                            "\n"
+                            "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
+                            "one remote, e.g. the 'origin' remote, consider setting\n"
+                            "checkout.defaultRemote=origin in your config."));
+           }
+
+           die(_("'%s' matched multiple (%d) remote tracking branches"),
+               arg, num_matches);
+       }
+
+       return remote;
+}
+
 static int parse_branchname_arg(int argc, const char **argv,
                                int dwim_new_local_branch_ok,
                                struct branch_info *new_branch_info,
                                struct checkout_opts *opts,
-                               struct object_id *rev,
-                               int *dwim_remotes_matched)
+                               struct object_id *rev)
 {
        const char **new_branch = &opts->new_branch;
        int argcount = 0;
@@ -1225,13 +1258,9 @@ static int parse_branchname_arg(int argc, const char **argv,
                        recover_with_dwim = 0;
 
                if (recover_with_dwim) {
-                       const char *remote = unique_tracking_name(arg, rev,
-                                                                 dwim_remotes_matched);
+                       const char *remote = parse_remote_branch(arg, rev,
+                                                                could_be_checkout_paths);
                        if (remote) {
-                               if (could_be_checkout_paths)
-                                       die(_("'%s' could be both a local file and a tracking branch.\n"
-                                             "Please use -- (and optionally --no-guess) to disambiguate"),
-                                           arg);
                                *new_branch = arg;
                                arg = remote;
                                /* DWIMmed to create local branch, case (3).(b) */
@@ -1496,7 +1525,6 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                         const char * const usagestr[])
 {
        struct branch_info new_branch_info;
-       int dwim_remotes_matched = 0;
        int parseopt_flags = 0;
 
        memset(&new_branch_info, 0, sizeof(new_branch_info));
@@ -1604,8 +1632,7 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
                        opts->track == BRANCH_TRACK_UNSPECIFIED &&
                        !opts->new_branch;
                int n = parse_branchname_arg(argc, argv, dwim_ok,
-                                            &new_branch_info, opts, &rev,
-                                            &dwim_remotes_matched);
+                                            &new_branch_info, opts, &rev);
                argv += n;
                argc -= n;
        } else if (!opts->accept_ref && opts->from_treeish) {
@@ -1682,28 +1709,10 @@ static int checkout_main(int argc, const char **argv, const char *prefix,
        }
 
        UNLEAK(opts);
-       if (opts->patch_mode || opts->pathspec.nr) {
-               int ret = checkout_paths(opts, new_branch_info.name);
-               if (ret && dwim_remotes_matched > 1 &&
-                   advice_checkout_ambiguous_remote_branch_name)
-                       advise(_("'%s' matched more than one remote tracking branch.\n"
-                                "We found %d remotes with a reference that matched. So we fell back\n"
-                                "on trying to resolve the argument as a path, but failed there too!\n"
-                                "\n"
-                                "If you meant to check out a remote tracking branch on, e.g. 'origin',\n"
-                                "you can do so by fully qualifying the name with the --track option:\n"
-                                "\n"
-                                "    git checkout --track origin/<name>\n"
-                                "\n"
-                                "If you'd like to always have checkouts of an ambiguous <name> prefer\n"
-                                "one remote, e.g. the 'origin' remote, consider setting\n"
-                                "checkout.defaultRemote=origin in your config."),
-                              argv[0],
-                              dwim_remotes_matched);
-               return ret;
-       } else {
+       if (opts->patch_mode || opts->pathspec.nr)
+               return checkout_paths(opts, new_branch_info.name);
+       else
                return checkout_branch(opts, &new_branch_info);
-       }
 }
 
 int cmd_checkout(int argc, const char **argv, const char *prefix)
index aa1332308a243802f2fb8bc98d276fd66510949b..c70ad01cc906cff61aae08ad4956f03aed7e96fc 100644 (file)
@@ -367,7 +367,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                die(_("index file corrupt"));
 
        if (interactive) {
-               char *old_index_env = NULL;
+               char *old_index_env = NULL, *old_repo_index_file;
                hold_locked_index(&index_lock, LOCK_DIE_ON_ERROR);
 
                refresh_cache_or_die(refresh_flags);
@@ -375,12 +375,16 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
                if (write_locked_index(&the_index, &index_lock, 0))
                        die(_("unable to create temporary index"));
 
+               old_repo_index_file = the_repository->index_file;
+               the_repository->index_file =
+                       (char *)get_lock_file_path(&index_lock);
                old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
-               setenv(INDEX_ENVIRONMENT, get_lock_file_path(&index_lock), 1);
+               setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
 
                if (interactive_add(argc, argv, prefix, patch_interactive) != 0)
                        die(_("interactive add failed"));
 
+               the_repository->index_file = old_repo_index_file;
                if (old_index_env && *old_index_env)
                        setenv(INDEX_ENVIRONMENT, old_index_env, 1);
                else
@@ -964,6 +968,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
         */
        if (!committable && whence != FROM_MERGE && !allow_empty &&
            !(amend && is_a_merge(current_head))) {
+               s->hints = advice_status_hints;
                s->display_comment_prefix = old_display_comment_prefix;
                run_status(stdout, index_file, prefix, 0, s);
                if (amend)
index 062e9114412504b86f36f8bf6601b77de88ada09..d127d2225f897f111124fb55b12cd1aae7db7a8e 100644 (file)
@@ -62,6 +62,7 @@ static int show_diffstat = 1, shortlog_len = -1, squash;
 static int option_commit = -1;
 static int option_edit = -1;
 static int allow_trivial = 1, have_message, verify_signatures;
+static int check_trust_level = 1;
 static int overwrite_ignore = 1;
 static struct strbuf merge_msg = STRBUF_INIT;
 static struct strategy **use_strategies;
@@ -631,6 +632,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
        } else if (!strcmp(k, "commit.gpgsign")) {
                sign_commit = git_config_bool(k, v) ? "" : NULL;
                return 0;
+       } else if (!strcmp(k, "gpg.mintrustlevel")) {
+               check_trust_level = 0;
        }
 
        status = fmt_merge_msg_config(k, v, cb);
@@ -1397,7 +1400,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        die(_("Can merge only exactly one commit into empty head"));
 
                if (verify_signatures)
-                       verify_merge_signature(remoteheads->item, verbosity);
+                       verify_merge_signature(remoteheads->item, verbosity,
+                                              check_trust_level);
 
                remote_head_oid = &remoteheads->item->object.oid;
                read_empty(remote_head_oid, 0);
@@ -1420,7 +1424,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
        if (verify_signatures) {
                for (p = remoteheads; p; p = p->next) {
-                       verify_merge_signature(p->item, verbosity);
+                       verify_merge_signature(p->item, verbosity,
+                                              check_trust_level);
                }
        }
 
index d25ff13a60f2d08efdc160c359364ab94a5b19ae..d4e3e77c8eb6daa0906f2e41ba3cacd7bc0200ba 100644 (file)
@@ -107,6 +107,7 @@ static char *opt_ff;
 static char *opt_verify_signatures;
 static int opt_autostash = -1;
 static int config_autostash;
+static int check_trust_level = 1;
 static struct argv_array opt_strategies = ARGV_ARRAY_INIT;
 static struct argv_array opt_strategy_opts = ARGV_ARRAY_INIT;
 static char *opt_gpg_sign;
@@ -355,6 +356,8 @@ static enum rebase_type config_get_rebase(void)
  */
 static int git_pull_config(const char *var, const char *value, void *cb)
 {
+       int status;
+
        if (!strcmp(var, "rebase.autostash")) {
                config_autostash = git_config_bool(var, value);
                return 0;
@@ -362,7 +365,14 @@ static int git_pull_config(const char *var, const char *value, void *cb)
                recurse_submodules = git_config_bool(var, value) ?
                        RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
                return 0;
+       } else if (!strcmp(var, "gpg.mintrustlevel")) {
+               check_trust_level = 0;
        }
+
+       status = git_gpg_config(var, value, cb);
+       if (status)
+               return status;
+
        return git_default_config(var, value, cb);
 }
 
@@ -587,7 +597,8 @@ static int pull_into_void(const struct object_id *merge_head,
                        die(_("unable to access commit %s"),
                            oid_to_hex(merge_head));
 
-               verify_merge_signature(commit, opt_verbosity);
+               verify_merge_signature(commit, opt_verbosity,
+                                      check_trust_level);
        }
 
        /*
index 4ad3adf4ba5a01d78f75dfc59b7ee99b25880e0c..879fc5f368346e0307e65d2a00d9fc4bad81ffa0 100644 (file)
@@ -998,9 +998,9 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
 {
        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 };
+       char *old_index_env = NULL, *old_repo_index_file;
 
        remove_path(stash_index_path.buf);
 
@@ -1014,16 +1014,19 @@ static int stash_patch(struct stash_info *info, const struct pathspec *ps,
        }
 
        /* 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;
-       }
+       old_repo_index_file = the_repository->index_file;
+       the_repository->index_file = stash_index_path.buf;
+       old_index_env = xstrdup_or_null(getenv(INDEX_ENVIRONMENT));
+       setenv(INDEX_ENVIRONMENT, the_repository->index_file, 1);
+
+       ret = run_add_interactive(NULL, "--patch=stash", ps);
+
+       the_repository->index_file = old_repo_index_file;
+       if (old_index_env && *old_index_env)
+               setenv(INDEX_ENVIRONMENT, old_index_env, 1);
+       else
+               unsetenv(INDEX_ENVIRONMENT);
+       FREE_AND_NULL(old_index_env);
 
        /* State of the working tree. */
        if (write_index_as_tree(&info->w_tree, &istate, stash_index_path.buf, 0,
index ff0ef7f08e759059a9e0053946f2f1cda3c26026..4df54c4efea8930e34f76205afa92b67e4275662 100755 (executable)
@@ -20,6 +20,7 @@ linux-gcc)
        export GIT_TEST_OE_DELTA_SIZE=5
        export GIT_TEST_COMMIT_GRAPH=1
        export GIT_TEST_MULTI_PACK_INDEX=1
+       export GIT_TEST_ADD_I_USE_BUILTIN=1
        make test
        ;;
 linux-gcc-4.8)
index 434ec030d6b2a0074c46c376c6322c92fd5c8a39..3f91d3efc5b697933289277606fbd2c053fca795 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -1136,21 +1136,23 @@ int check_commit_signature(const struct commit *commit, struct signature_check *
        return ret;
 }
 
-void verify_merge_signature(struct commit *commit, int verbosity)
+void verify_merge_signature(struct commit *commit, int verbosity,
+                           int check_trust)
 {
        char hex[GIT_MAX_HEXSZ + 1];
        struct signature_check signature_check;
+       int ret;
        memset(&signature_check, 0, sizeof(signature_check));
 
-       check_commit_signature(commit, &signature_check);
+       ret = check_commit_signature(commit, &signature_check);
 
        find_unique_abbrev_r(hex, &commit->object.oid, DEFAULT_ABBREV);
        switch (signature_check.result) {
        case 'G':
+               if (ret || (check_trust && signature_check.trust_level < TRUST_MARGINAL))
+                       die(_("Commit %s has an untrusted GPG signature, "
+                             "allegedly by %s."), hex, signature_check.signer);
                break;
-       case 'U':
-               die(_("Commit %s has an untrusted GPG signature, "
-                     "allegedly by %s."), hex, signature_check.signer);
        case 'B':
                die(_("Commit %s has a bad GPG signature "
                      "allegedly by %s."), hex, signature_check.signer);
index 221cdaa34b826bc160048567ffdf4872e1b37c47..008a0fa4a01d06c0e191e5988823ff630931d30d 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -383,8 +383,18 @@ int compare_commits_by_author_date(const void *a_, const void *b_, void *unused)
  * Verify a single commit with check_commit_signature() and die() if it is not
  * a good signature. This isn't really suitable for general use, but is a
  * helper to implement consistent logic for pull/merge --verify-signatures.
+ *
+ * The check_trust parameter is meant for backward-compatibility.  The GPG
+ * interface verifies key trust with a default trust level that is below the
+ * default trust level for merge operations.  Its value should be non-zero if
+ * the user hasn't set a minimum trust level explicitly in their configuration.
+ *
+ * If the user has set a minimum trust level, then that value should be obeyed
+ * and check_trust should be zero, even if the configured trust level is below
+ * the default trust level for merges.
  */
-void verify_merge_signature(struct commit *commit, int verbose);
+void verify_merge_signature(struct commit *commit, int verbose,
+                           int check_trust);
 
 int compare_commits_by_commit_date(const void *a_, const void *b_, void *unused);
 int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused);
index 08a26096637712689a071bf58586939bb6ff7c75..2d3412860d4d3a10cb989a769260496326055924 100644 (file)
 extern "C" {
 #endif
 
+#define regcomp git_regcomp
+#define regexec git_regexec
+#define regerror git_regerror
+#define regfree git_regfree
+
 /* The following two types have to be signed and unsigned integer type
    wide enough to hold a value of a pointer.  For most ANSI compilers
    ptrdiff_t and size_t should be likely OK.  Still size of these two
index fa13ee672db33ef2fbcc16295230b99aa9d4f595..35bca03d1470601568d17ed3cf71c5c622d43d87 100644 (file)
@@ -2,6 +2,9 @@
 #include "compat/terminal.h"
 #include "sigchain.h"
 #include "strbuf.h"
+#include "run-command.h"
+#include "string-list.h"
+#include "hashmap.h"
 
 #if defined(HAVE_DEV_TTY) || defined(GIT_WINDOWS_NATIVE)
 
@@ -32,7 +35,7 @@ static void restore_term(void)
        term_fd = -1;
 }
 
-static int disable_echo(void)
+static int disable_bits(tcflag_t bits)
 {
        struct termios t;
 
@@ -43,7 +46,7 @@ static int disable_echo(void)
        old_term = t;
        sigchain_push_common(restore_term_on_signal);
 
-       t.c_lflag &= ~ECHO;
+       t.c_lflag &= ~bits;
        if (!tcsetattr(term_fd, TCSAFLUSH, &t))
                return 0;
 
@@ -53,17 +56,44 @@ error:
        return -1;
 }
 
+static int disable_echo(void)
+{
+       return disable_bits(ECHO);
+}
+
+static int enable_non_canonical(void)
+{
+       return disable_bits(ICANON | ECHO);
+}
+
 #elif defined(GIT_WINDOWS_NATIVE)
 
 #define INPUT_PATH "CONIN$"
 #define OUTPUT_PATH "CONOUT$"
 #define FORCE_TEXT "t"
 
+static int use_stty = 1;
+static struct string_list stty_restore = STRING_LIST_INIT_DUP;
 static HANDLE hconin = INVALID_HANDLE_VALUE;
 static DWORD cmode;
 
 static void restore_term(void)
 {
+       if (use_stty) {
+               int i;
+               struct child_process cp = CHILD_PROCESS_INIT;
+
+               if (stty_restore.nr == 0)
+                       return;
+
+               argv_array_push(&cp.args, "stty");
+               for (i = 0; i < stty_restore.nr; i++)
+                       argv_array_push(&cp.args, stty_restore.items[i].string);
+               run_command(&cp);
+               string_list_clear(&stty_restore, 0);
+               return;
+       }
+
        if (hconin == INVALID_HANDLE_VALUE)
                return;
 
@@ -72,8 +102,39 @@ static void restore_term(void)
        hconin = INVALID_HANDLE_VALUE;
 }
 
-static int disable_echo(void)
+static int disable_bits(DWORD bits)
 {
+       if (use_stty) {
+               struct child_process cp = CHILD_PROCESS_INIT;
+
+               argv_array_push(&cp.args, "stty");
+
+               if (bits & ENABLE_LINE_INPUT) {
+                       string_list_append(&stty_restore, "icanon");
+                       argv_array_push(&cp.args, "-icanon");
+               }
+
+               if (bits & ENABLE_ECHO_INPUT) {
+                       string_list_append(&stty_restore, "echo");
+                       argv_array_push(&cp.args, "-echo");
+               }
+
+               if (bits & ENABLE_PROCESSED_INPUT) {
+                       string_list_append(&stty_restore, "-ignbrk");
+                       string_list_append(&stty_restore, "intr");
+                       string_list_append(&stty_restore, "^c");
+                       argv_array_push(&cp.args, "ignbrk");
+                       argv_array_push(&cp.args, "intr");
+                       argv_array_push(&cp.args, "");
+               }
+
+               if (run_command(&cp) == 0)
+                       return 0;
+
+               /* `stty` could not be executed; access the Console directly */
+               use_stty = 0;
+       }
+
        hconin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE,
            FILE_SHARE_READ, NULL, OPEN_EXISTING,
            FILE_ATTRIBUTE_NORMAL, NULL);
@@ -82,7 +143,7 @@ static int disable_echo(void)
 
        GetConsoleMode(hconin, &cmode);
        sigchain_push_common(restore_term_on_signal);
-       if (!SetConsoleMode(hconin, cmode & (~ENABLE_ECHO_INPUT))) {
+       if (!SetConsoleMode(hconin, cmode & ~bits)) {
                CloseHandle(hconin);
                hconin = INVALID_HANDLE_VALUE;
                return -1;
@@ -91,6 +152,47 @@ static int disable_echo(void)
        return 0;
 }
 
+static int disable_echo(void)
+{
+       return disable_bits(ENABLE_ECHO_INPUT);
+}
+
+static int enable_non_canonical(void)
+{
+       return disable_bits(ENABLE_ECHO_INPUT | ENABLE_LINE_INPUT | ENABLE_PROCESSED_INPUT);
+}
+
+/*
+ * Override `getchar()`, as the default implementation does not use
+ * `ReadFile()`.
+ *
+ * This poses a problem when we want to see whether the standard
+ * input has more characters, as the default of Git for Windows is to start the
+ * Bash in a MinTTY, which uses a named pipe to emulate a pty, in which case
+ * our `poll()` emulation calls `PeekNamedPipe()`, which seems to require
+ * `ReadFile()` to be called first to work properly (it only reports 0
+ * available bytes, otherwise).
+ *
+ * So let's just override `getchar()` with a version backed by `ReadFile()` and
+ * go our merry ways from here.
+ */
+static int mingw_getchar(void)
+{
+       DWORD read = 0;
+       unsigned char ch;
+
+       if (!ReadFile(GetStdHandle(STD_INPUT_HANDLE), &ch, 1, &read, NULL))
+               return EOF;
+
+       if (!read) {
+               error("Unexpected 0 read");
+               return EOF;
+       }
+
+       return ch;
+}
+#define getchar mingw_getchar
+
 #endif
 
 #ifndef FORCE_TEXT
@@ -137,6 +239,126 @@ char *git_terminal_prompt(const char *prompt, int echo)
        return buf.buf;
 }
 
+/*
+ * The `is_known_escape_sequence()` function returns 1 if the passed string
+ * corresponds to an Escape sequence that the terminal capabilities contains.
+ *
+ * To avoid depending on ncurses or other platform-specific libraries, we rely
+ * on the presence of the `infocmp` executable to do the job for us (failing
+ * silently if the program is not available or refused to run).
+ */
+struct escape_sequence_entry {
+       struct hashmap_entry entry;
+       char sequence[FLEX_ARRAY];
+};
+
+static int sequence_entry_cmp(const void *hashmap_cmp_fn_data,
+                             const struct escape_sequence_entry *e1,
+                             const struct escape_sequence_entry *e2,
+                             const void *keydata)
+{
+       return strcmp(e1->sequence, keydata ? keydata : e2->sequence);
+}
+
+static int is_known_escape_sequence(const char *sequence)
+{
+       static struct hashmap sequences;
+       static int initialized;
+
+       if (!initialized) {
+               struct child_process cp = CHILD_PROCESS_INIT;
+               struct strbuf buf = STRBUF_INIT;
+               char *p, *eol;
+
+               hashmap_init(&sequences, (hashmap_cmp_fn)sequence_entry_cmp,
+                            NULL, 0);
+
+               argv_array_pushl(&cp.args, "infocmp", "-L", "-1", NULL);
+               if (pipe_command(&cp, NULL, 0, &buf, 0, NULL, 0))
+                       strbuf_setlen(&buf, 0);
+
+               for (eol = p = buf.buf; *p; p = eol + 1) {
+                       p = strchr(p, '=');
+                       if (!p)
+                               break;
+                       p++;
+                       eol = strchrnul(p, '\n');
+
+                       if (starts_with(p, "\\E")) {
+                               char *comma = memchr(p, ',', eol - p);
+                               struct escape_sequence_entry *e;
+
+                               p[0] = '^';
+                               p[1] = '[';
+                               FLEX_ALLOC_MEM(e, sequence, p, comma - p);
+                               hashmap_entry_init(&e->entry,
+                                                  strhash(e->sequence));
+                               hashmap_add(&sequences, &e->entry);
+                       }
+                       if (!*eol)
+                               break;
+               }
+               initialized = 1;
+       }
+
+       return !!hashmap_get_from_hash(&sequences, strhash(sequence), sequence);
+}
+
+int read_key_without_echo(struct strbuf *buf)
+{
+       static int warning_displayed;
+       int ch;
+
+       if (warning_displayed || enable_non_canonical() < 0) {
+               if (!warning_displayed) {
+                       warning("reading single keystrokes not supported on "
+                               "this platform; reading line instead");
+                       warning_displayed = 1;
+               }
+
+               return strbuf_getline(buf, stdin);
+       }
+
+       strbuf_reset(buf);
+       ch = getchar();
+       if (ch == EOF) {
+               restore_term();
+               return EOF;
+       }
+       strbuf_addch(buf, ch);
+
+       if (ch == '\033' /* ESC */) {
+               /*
+                * We are most likely looking at an Escape sequence. Let's try
+                * to read more bytes, waiting at most half a second, assuming
+                * that the sequence is complete if we did not receive any byte
+                * within that time.
+                *
+                * Start by replacing the Escape byte with ^[ */
+               strbuf_splice(buf, buf->len - 1, 1, "^[", 2);
+
+               /*
+                * Query the terminal capabilities once about all the Escape
+                * sequences it knows about, so that we can avoid waiting for
+                * half a second when we know that the sequence is complete.
+                */
+               while (!is_known_escape_sequence(buf->buf)) {
+                       struct pollfd pfd = { .fd = 0, .events = POLLIN };
+
+                       if (poll(&pfd, 1, 500) < 1)
+                               break;
+
+                       ch = getchar();
+                       if (ch == EOF)
+                               return 0;
+                       strbuf_addch(buf, ch);
+               }
+       }
+
+       restore_term();
+       return 0;
+}
+
 #else
 
 char *git_terminal_prompt(const char *prompt, int echo)
@@ -144,4 +366,23 @@ char *git_terminal_prompt(const char *prompt, int echo)
        return getpass(prompt);
 }
 
+int read_key_without_echo(struct strbuf *buf)
+{
+       static int warning_displayed;
+       const char *res;
+
+       if (!warning_displayed) {
+               warning("reading single keystrokes not supported on this "
+                       "platform; reading line instead");
+               warning_displayed = 1;
+       }
+
+       res = getpass("");
+       strbuf_reset(buf);
+       if (!res)
+               return EOF;
+       strbuf_addstr(buf, res);
+       return 0;
+}
+
 #endif
index 97db7cd69d65fc1a03ffb25e4b53ad6d296016e5..a9d52b8464e2f6e3c39bc107078cb5b2a36aa5d5 100644 (file)
@@ -3,4 +3,7 @@
 
 char *git_terminal_prompt(const char *prompt, int echo);
 
+/* Read a single keystroke, without echoing it to the terminal */
+int read_key_without_echo(struct strbuf *buf);
+
 #endif /* COMPAT_TERMINAL_H */
index e4d9ff4a95ceb56e568131c148de5aaed85219c2..1aac5a56c0637cf09b68f2232e061fe0755703c3 100644 (file)
@@ -1069,15 +1069,32 @@ __git_aliased_command ()
        done
 }
 
-# __git_find_on_cmdline requires 1 argument
+# Check whether one of the given words is present on the command line,
+# and print the first word found.
+#
+# Usage: __git_find_on_cmdline [<option>]... "<wordlist>"
+# --show-idx: Optionally show the index of the found word in the $words array.
 __git_find_on_cmdline ()
 {
-       local word subcommand c=1
+       local word c=1 show_idx
+
+       while test $# -gt 1; do
+               case "$1" in
+               --show-idx)     show_idx=y ;;
+               *)              return 1 ;;
+               esac
+               shift
+       done
+       local wordlist="$1"
+
        while [ $c -lt $cword ]; do
-               word="${words[c]}"
-               for subcommand in $1; do
-                       if [ "$subcommand" = "$word" ]; then
-                               echo "$subcommand"
+               for word in $wordlist; do
+                       if [ "$word" = "${words[c]}" ]; then
+                               if [ -n "$show_idx" ]; then
+                                       echo "$c $word"
+                               else
+                                       echo "$word"
+                               fi
                                return
                        fi
                done
@@ -2718,6 +2735,27 @@ _git_show_branch ()
        __git_complete_revlist
 }
 
+_git_sparse_checkout ()
+{
+       local subcommands="list init set disable"
+       local subcommand="$(__git_find_on_cmdline "$subcommands")"
+       if [ -z "$subcommand" ]; then
+               __gitcomp "$subcommands"
+               return
+       fi
+
+       case "$subcommand,$cur" in
+       init,--*)
+               __gitcomp "--cone"
+               ;;
+       set,--*)
+               __gitcomp "--stdin"
+               ;;
+       *)
+               ;;
+       esac
+}
+
 _git_stash ()
 {
        local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked'
@@ -2969,33 +3007,83 @@ _git_whatchanged ()
        _git_log
 }
 
+__git_complete_worktree_paths ()
+{
+       local IFS=$'\n'
+       __gitcomp_nl "$(git worktree list --porcelain |
+               # Skip the first entry: it's the path of the main worktree,
+               # which can't be moved, removed, locked, etc.
+               sed -n -e '2,$ s/^worktree //p')"
+}
+
 _git_worktree ()
 {
        local subcommands="add list lock move prune remove unlock"
-       local subcommand="$(__git_find_on_cmdline "$subcommands")"
-       if [ -z "$subcommand" ]; then
+       local subcommand subcommand_idx
+
+       subcommand="$(__git_find_on_cmdline --show-idx "$subcommands")"
+       subcommand_idx="${subcommand% *}"
+       subcommand="${subcommand#* }"
+
+       case "$subcommand,$cur" in
+       ,*)
                __gitcomp "$subcommands"
-       else
-               case "$subcommand,$cur" in
-               add,--*)
-                       __gitcomp_builtin worktree_add
-                       ;;
-               list,--*)
-                       __gitcomp_builtin worktree_list
-                       ;;
-               lock,--*)
-                       __gitcomp_builtin worktree_lock
-                       ;;
-               prune,--*)
-                       __gitcomp_builtin worktree_prune
-                       ;;
-               remove,--*)
-                       __gitcomp "--force"
+               ;;
+       *,--*)
+               __gitcomp_builtin worktree_$subcommand
+               ;;
+       add,*)  # usage: git worktree add [<options>] <path> [<commit-ish>]
+               # Here we are not completing an --option, it's either the
+               # path or a ref.
+               case "$prev" in
+               -b|-B)  # Complete refs for branch to be created/reseted.
+                       __git_complete_refs
                        ;;
-               *)
+               -*)     # The previous word is an -o|--option without an
+                       # unstuck argument: have to complete the path for
+                       # the new worktree, so don't list anything, but let
+                       # Bash fall back to filename completion.
+                       ;;
+               *)      # The previous word is not an --option, so it must
+                       # be either the 'add' subcommand, the unstuck
+                       # argument of an option (e.g. branch for -b|-B), or
+                       # the path for the new worktree.
+                       if [ $cword -eq $((subcommand_idx+1)) ]; then
+                               # Right after the 'add' subcommand: have to
+                               # complete the path, so fall back to Bash
+                               # filename completion.
+                               :
+                       else
+                               case "${words[cword-2]}" in
+                               -b|-B)  # After '-b <branch>': have to
+                                       # complete the path, so fall back
+                                       # to Bash filename completion.
+                                       ;;
+                               *)      # After the path: have to complete
+                                       # the ref to be checked out.
+                                       __git_complete_refs
+                                       ;;
+                               esac
+                       fi
                        ;;
                esac
-       fi
+               ;;
+       lock,*|remove,*|unlock,*)
+               __git_complete_worktree_paths
+               ;;
+       move,*)
+               if [ $cword -eq $((subcommand_idx+1)) ]; then
+                       # The first parameter must be an existing working
+                       # tree to be moved.
+                       __git_complete_worktree_paths
+               else
+                       # The second parameter is the destination: it could
+                       # be any path, so don't list anything, but let Bash
+                       # fall back to filename completion.
+                       :
+               fi
+               ;;
+       esac
 }
 
 __git_complete_common () {
diff --git a/contrib/credential/netrc/.gitignore b/contrib/credential/netrc/.gitignore
new file mode 100644 (file)
index 0000000..d41cdde
--- /dev/null
@@ -0,0 +1 @@
+git-credential-netrc
index 6174e3bb83826273f005b01f6d9e9b2093664e33..c284fb8ac49012981d3fcde4e75f46a3e2784e32 100644 (file)
@@ -1,8 +1,30 @@
 # The default target of this Makefile is...
 all::
 
-test:
+SCRIPT_PERL = git-credential-netrc.perl
+GIT_ROOT_DIR = ../../..
+HERE = contrib/credential/netrc
+
+SCRIPT_PERL_FULL = $(patsubst %,$(HERE)/%,$(SCRIPT_PERL))
+
+all:: build
+
+build:
+       $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                build-perl-script
+
+install: build
+       $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                install-perl-script
+
+clean:
+       $(MAKE) -C $(GIT_ROOT_DIR) SCRIPT_PERL="$(SCRIPT_PERL_FULL)" \
+                clean-perl-script
+
+test: build
        ./t-git-credential-netrc.sh
 
-testverbose:
+testverbose: build
        ./t-git-credential-netrc.sh -d -v
+
+.PHONY: all build install clean test testverbose
similarity index 99%
rename from contrib/credential/netrc/git-credential-netrc
rename to contrib/credential/netrc/git-credential-netrc.perl
index ebfc123ec641ddfa1bb2aaa83dab268c19083b2a..bc57cc65884b97dc6a0d33a067f00dac35ee19a8 100755 (executable)
@@ -423,7 +423,7 @@ sub load_config {
        # load settings from git config
        my $options = shift;
        # set from command argument, gpg.program option, or default to gpg
-       $options->{'gpg'} //= Git->repository()->config('gpg.program')
+       $options->{'gpg'} //= Git::config('gpg.program')
                          // 'gpg';
        log_verbose("using $options{'gpg'} for GPG operations");
 }
diff --git a/dir.c b/dir.c
index 7d255227b130d7729747fe1a86c8efc04eee9f7e..b460211e6149707b813d9354154233272cdfd87d 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -41,7 +41,8 @@ struct cached_dir {
        int nr_files;
        int nr_dirs;
 
-       struct dirent *de;
+       const char *d_name;
+       int d_type;
        const char *file;
        struct untracked_cache_dir *ucd;
 };
@@ -50,8 +51,8 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
        struct index_state *istate, const char *path, int len,
        struct untracked_cache_dir *untracked,
        int check_only, int stop_at_first_file, const struct pathspec *pathspec);
-static int get_dtype(struct dirent *de, struct index_state *istate,
-                    const char *path, int len);
+static int resolve_dtype(int dtype, struct index_state *istate,
+                        const char *path, int len);
 
 int count_slashes(const char *s)
 {
@@ -1215,8 +1216,7 @@ static struct path_pattern *last_matching_pattern_from_list(const char *pathname
                int prefix = pattern->nowildcardlen;
 
                if (pattern->flags & PATTERN_FLAG_MUSTBEDIR) {
-                       if (*dtype == DT_UNKNOWN)
-                               *dtype = get_dtype(NULL, istate, pathname, pathlen);
+                       *dtype = resolve_dtype(*dtype, istate, pathname, pathlen);
                        if (*dtype != DT_DIR)
                                continue;
                }
@@ -1842,10 +1842,9 @@ static int get_index_dtype(struct index_state *istate,
        return DT_UNKNOWN;
 }
 
-static int get_dtype(struct dirent *de, struct index_state *istate,
-                    const char *path, int len)
+static int resolve_dtype(int dtype, struct index_state *istate,
+                        const char *path, int len)
 {
-       int dtype = de ? DTYPE(de) : DT_UNKNOWN;
        struct stat st;
 
        if (dtype != DT_UNKNOWN)
@@ -1870,14 +1869,13 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                                          struct strbuf *path,
                                          int baselen,
                                          const struct pathspec *pathspec,
-                                         int dtype, struct dirent *de)
+                                         int dtype)
 {
        int exclude;
        int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);
        enum path_treatment path_treatment;
 
-       if (dtype == DT_UNKNOWN)
-               dtype = get_dtype(de, istate, path->buf, path->len);
+       dtype = resolve_dtype(dtype, istate, path->buf, path->len);
 
        /* Always exclude indexed files */
        if (dtype != DT_DIR && has_path_in_index)
@@ -1985,21 +1983,18 @@ static enum path_treatment treat_path(struct dir_struct *dir,
                                      int baselen,
                                      const struct pathspec *pathspec)
 {
-       int dtype;
-       struct dirent *de = cdir->de;
-
-       if (!de)
+       if (!cdir->d_name)
                return treat_path_fast(dir, untracked, cdir, istate, path,
                                       baselen, pathspec);
-       if (is_dot_or_dotdot(de->d_name) || !fspathcmp(de->d_name, ".git"))
+       if (is_dot_or_dotdot(cdir->d_name) || !fspathcmp(cdir->d_name, ".git"))
                return path_none;
        strbuf_setlen(path, baselen);
-       strbuf_addstr(path, de->d_name);
+       strbuf_addstr(path, cdir->d_name);
        if (simplify_away(path->buf, path->len, pathspec))
                return path_none;
 
-       dtype = DTYPE(de);
-       return treat_one_path(dir, untracked, istate, path, baselen, pathspec, dtype, de);
+       return treat_one_path(dir, untracked, istate, path, baselen, pathspec,
+                             cdir->d_type);
 }
 
 static void add_untracked(struct untracked_cache_dir *dir, const char *name)
@@ -2087,10 +2082,17 @@ static int open_cached_dir(struct cached_dir *cdir,
 
 static int read_cached_dir(struct cached_dir *cdir)
 {
+       struct dirent *de;
+
        if (cdir->fdir) {
-               cdir->de = readdir(cdir->fdir);
-               if (!cdir->de)
+               de = readdir(cdir->fdir);
+               if (!de) {
+                       cdir->d_name = NULL;
+                       cdir->d_type = DT_UNKNOWN;
                        return -1;
+               }
+               cdir->d_name = de->d_name;
+               cdir->d_type = DTYPE(de);
                return 0;
        }
        while (cdir->nr_dirs < cdir->untracked->dirs_nr) {
@@ -2216,7 +2218,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
                /* recurse into subdir if instructed by treat_path */
                if ((state == path_recurse) ||
                        ((state == path_untracked) &&
-                        (get_dtype(cdir.de, istate, path.buf, path.len) == DT_DIR) &&
+                        (resolve_dtype(cdir.d_type, istate, path.buf, path.len) == DT_DIR) &&
                         ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
                          (pathspec &&
                           do_match_pathspec(istate, pathspec, path.buf, path.len,
@@ -2308,16 +2310,16 @@ static int treat_leading_path(struct dir_struct *dir,
         * WARNING WARNING WARNING:
         *
         * Any updates to the traversal logic here may need corresponding
-        * updates in treat_leading_path().  See the commit message for the
-        * commit adding this warning as well as the commit preceding it
-        * for details.
+        * updates in read_directory_recursive().  See 777b420347 (dir:
+        * synchronize treat_leading_path() and read_directory_recursive(),
+        * 2019-12-19) and its parent commit for details.
         */
 
        struct strbuf sb = STRBUF_INIT;
+       struct strbuf subdir = STRBUF_INIT;
        int prevlen, baselen;
        const char *cp;
        struct cached_dir cdir;
-       struct dirent *de;
        enum path_treatment state = path_none;
 
        /*
@@ -2342,22 +2344,8 @@ static int treat_leading_path(struct dir_struct *dir,
        if (!len)
                return 1;
 
-       /*
-        * We need a manufactured dirent with sufficient space to store a
-        * leading directory component of path in its d_name.  Here, we
-        * assume that the dirent's d_name is either declared as
-        *    char d_name[BIG_ENOUGH]
-        * or that it is declared at the end of the struct as
-        *    char d_name[]
-        * For either case, padding with len+1 bytes at the end will ensure
-        * sufficient storage space.
-        */
-       de = xcalloc(1, st_add3(sizeof(struct dirent), len, 1));
        memset(&cdir, 0, sizeof(cdir));
-       cdir.de = de;
-#if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
-       de->d_type = DT_DIR;
-#endif
+       cdir.d_type = DT_DIR;
        baselen = 0;
        prevlen = 0;
        while (1) {
@@ -2374,15 +2362,20 @@ static int treat_leading_path(struct dir_struct *dir,
                        break;
                strbuf_reset(&sb);
                strbuf_add(&sb, path, prevlen);
-               memcpy(de->d_name, path+prevlen, baselen-prevlen);
-               de->d_name[baselen-prevlen] = '\0';
+               strbuf_reset(&subdir);
+               strbuf_add(&subdir, path+prevlen, baselen-prevlen);
+               cdir.d_name = subdir.buf;
                state = treat_path(dir, NULL, &cdir, istate, &sb, prevlen,
                                    pathspec);
                if (state == path_untracked &&
-                   get_dtype(cdir.de, istate, sb.buf, sb.len) == DT_DIR &&
+                   resolve_dtype(cdir.d_type, istate, sb.buf, sb.len) == DT_DIR &&
                    (dir->flags & DIR_SHOW_IGNORED_TOO ||
                     do_match_pathspec(istate, pathspec, sb.buf, sb.len,
                                       baselen, NULL, DO_MATCH_LEADING_PATHSPEC) == MATCHED_RECURSIVELY_LEADING_PATHSPEC)) {
+                       if (!match_pathspec(istate, pathspec, sb.buf, sb.len,
+                                           0 /* prefix */, NULL,
+                                           0 /* do NOT special case dirs */))
+                               state = path_none;
                        add_path_to_appropriate_result_list(dir, NULL, &cdir,
                                                            istate,
                                                            &sb, baselen,
@@ -2399,7 +2392,7 @@ static int treat_leading_path(struct dir_struct *dir,
                                            &sb, baselen, pathspec,
                                            state);
 
-       free(de);
+       strbuf_release(&subdir);
        strbuf_release(&sb);
        return state == path_recurse;
 }
index 53fa5743018f19d8428f4a24379907bcbb6d80bf..4d4ebb4f2ba54e3061cc8ade350a9fb2d9421690 100755 (executable)
@@ -207,7 +207,7 @@ create_stash () {
 
                # find out what the user wants
                GIT_INDEX_FILE="$TMP-index" \
-                       git add--interactive --patch=stash -- "$@" &&
+                       git add --legacy-stash-p -- "$@" &&
 
                # state of the working tree
                w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
index aaa1809d243c6671a9c6fc959d96bd366a65efec..afcb4c09481c936b21e55b2ebb3802343a9a7c9e 100755 (executable)
@@ -241,13 +241,15 @@ cmd_add()
            die "$(eval_gettext "'\$sm_path' does not have a commit checked out")"
        fi
 
-       if test -z "$force" &&
-               ! git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" > /dev/null 2>&1
+       if test -z "$force"
        then
-               eval_gettextln "The following path is ignored by one of your .gitignore files:
-\$sm_path
-Use -f if you really want to add it." >&2
-               exit 1
+           dryerr=$(git add --dry-run --ignore-missing --no-warn-embedded-repo "$sm_path" 2>&1 >/dev/null)
+           res=$?
+           if test $res -ne 0
+           then
+                echo >&2 "$dryerr"
+                exit $res
+           fi
        fi
 
        if test -n "$custom_name"
index 5134ce27806866c41d5eda19cf01110cbf7849f2..2d538bcd6e30d1314f76d89764284f8735cd187f 100644 (file)
@@ -7,6 +7,8 @@
 #include "tempfile.h"
 
 static char *configured_signing_key;
+static enum signature_trust_level configured_min_trust_level = TRUST_UNDEFINED;
+
 struct gpg_format {
        const char *name;
        const char *program;
@@ -85,6 +87,8 @@ void signature_check_clear(struct signature_check *sigc)
 #define GPG_STATUS_UID         (1<<2)
 /* The status includes key fingerprints */
 #define GPG_STATUS_FINGERPRINT (1<<3)
+/* The status includes trust level */
+#define GPG_STATUS_TRUST_LEVEL (1<<4)
 
 /* Short-hand for standard exclusive *SIG status with keyid & UID */
 #define GPG_STATUS_STDSIG      (GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID|GPG_STATUS_UID)
@@ -96,13 +100,23 @@ static struct {
 } sigcheck_gpg_status[] = {
        { 'G', "GOODSIG ", GPG_STATUS_STDSIG },
        { 'B', "BADSIG ", GPG_STATUS_STDSIG },
-       { 'U', "TRUST_NEVER", 0 },
-       { 'U', "TRUST_UNDEFINED", 0 },
        { 'E', "ERRSIG ", GPG_STATUS_EXCLUSIVE|GPG_STATUS_KEYID },
        { 'X', "EXPSIG ", GPG_STATUS_STDSIG },
        { 'Y', "EXPKEYSIG ", GPG_STATUS_STDSIG },
        { 'R', "REVKEYSIG ", GPG_STATUS_STDSIG },
        { 0, "VALIDSIG ", GPG_STATUS_FINGERPRINT },
+       { 0, "TRUST_", GPG_STATUS_TRUST_LEVEL },
+};
+
+static struct {
+       const char *key;
+       enum signature_trust_level value;
+} sigcheck_gpg_trust_level[] = {
+       { "UNDEFINED", TRUST_UNDEFINED },
+       { "NEVER", TRUST_NEVER },
+       { "MARGINAL", TRUST_MARGINAL },
+       { "FULLY", TRUST_FULLY },
+       { "ULTIMATE", TRUST_ULTIMATE },
 };
 
 static void replace_cstring(char **field, const char *line, const char *next)
@@ -115,6 +129,20 @@ static void replace_cstring(char **field, const char *line, const char *next)
                *field = NULL;
 }
 
+static int parse_gpg_trust_level(const char *level,
+                                enum signature_trust_level *res)
+{
+       size_t i;
+
+       for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_trust_level); i++) {
+               if (!strcmp(sigcheck_gpg_trust_level[i].key, level)) {
+                       *res = sigcheck_gpg_trust_level[i].value;
+                       return 0;
+               }
+       }
+       return 1;
+}
+
 static void parse_gpg_output(struct signature_check *sigc)
 {
        const char *buf = sigc->gpg_status;
@@ -136,9 +164,18 @@ static void parse_gpg_output(struct signature_check *sigc)
                /* Iterate over all search strings */
                for (i = 0; i < ARRAY_SIZE(sigcheck_gpg_status); i++) {
                        if (skip_prefix(line, sigcheck_gpg_status[i].check, &line)) {
+                               /*
+                                * GOODSIG, BADSIG etc. can occur only once for
+                                * each signature.  Therefore, if we had more
+                                * than one then we're dealing with multiple
+                                * signatures.  We don't support them
+                                * currently, and they're rather hard to
+                                * create, so something is likely fishy and we
+                                * should reject them altogether.
+                                */
                                if (sigcheck_gpg_status[i].flags & GPG_STATUS_EXCLUSIVE) {
                                        if (seen_exclusive_status++)
-                                               goto found_duplicate_status;
+                                               goto error;
                                }
 
                                if (sigcheck_gpg_status[i].result)
@@ -154,6 +191,25 @@ static void parse_gpg_output(struct signature_check *sigc)
                                                replace_cstring(&sigc->signer, line, next);
                                        }
                                }
+
+                               /* Do we have trust level? */
+                               if (sigcheck_gpg_status[i].flags & GPG_STATUS_TRUST_LEVEL) {
+                                       /*
+                                        * GPG v1 and v2 differs in how the
+                                        * TRUST_ lines are written.  Some
+                                        * trust lines contain no additional
+                                        * space-separated information for v1.
+                                        */
+                                       size_t trust_size = strcspn(line, " \n");
+                                       char *trust = xmemdupz(line, trust_size);
+
+                                       if (parse_gpg_trust_level(trust, &sigc->trust_level)) {
+                                               free(trust);
+                                               goto error;
+                                       }
+                                       free(trust);
+                               }
+
                                /* Do we have fingerprint? */
                                if (sigcheck_gpg_status[i].flags & GPG_STATUS_FINGERPRINT) {
                                        const char *limit;
@@ -191,14 +247,7 @@ static void parse_gpg_output(struct signature_check *sigc)
        }
        return;
 
-found_duplicate_status:
-       /*
-        * GOODSIG, BADSIG etc. can occur only once for each signature.
-        * Therefore, if we had more than one then we're dealing with multiple
-        * signatures.  We don't support them currently, and they're rather
-        * hard to create, so something is likely fishy and we should reject
-        * them altogether.
-        */
+error:
        sigc->result = 'E';
        /* Clear partial data to avoid confusion */
        FREE_AND_NULL(sigc->primary_key_fingerprint);
@@ -264,6 +313,7 @@ int check_signature(const char *payload, size_t plen, const char *signature,
        int status;
 
        sigc->result = 'N';
+       sigc->trust_level = -1;
 
        status = verify_signed_buffer(payload, plen, signature, slen,
                                      &gpg_output, &gpg_status);
@@ -273,7 +323,8 @@ int check_signature(const char *payload, size_t plen, const char *signature,
        sigc->gpg_output = strbuf_detach(&gpg_output, NULL);
        sigc->gpg_status = strbuf_detach(&gpg_status, NULL);
        parse_gpg_output(sigc);
-       status |= sigc->result != 'G' && sigc->result != 'U';
+       status |= sigc->result != 'G';
+       status |= sigc->trust_level < configured_min_trust_level;
 
  out:
        strbuf_release(&gpg_status);
@@ -320,6 +371,8 @@ int git_gpg_config(const char *var, const char *value, void *cb)
 {
        struct gpg_format *fmt = NULL;
        char *fmtname = NULL;
+       char *trust;
+       int ret;
 
        if (!strcmp(var, "user.signingkey")) {
                if (!value)
@@ -339,6 +392,20 @@ int git_gpg_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
+       if (!strcmp(var, "gpg.mintrustlevel")) {
+               if (!value)
+                       return config_error_nonbool(var);
+
+               trust = xstrdup_toupper(value);
+               ret = parse_gpg_trust_level(trust, &configured_min_trust_level);
+               free(trust);
+
+               if (ret)
+                       return error("unsupported value for %s: %s", var,
+                                    value);
+               return 0;
+       }
+
        if (!strcmp(var, "gpg.program") || !strcmp(var, "gpg.openpgp.program"))
                fmtname = "openpgp";
 
index 93cc3aff5c93ce8fa19aadea909960f9267edbdc..f4e9b4f3715a0b0d4be3ef526388bf6b87b56fda 100644 (file)
@@ -7,6 +7,14 @@ struct strbuf;
 #define GPG_VERIFY_RAW                 2
 #define GPG_VERIFY_OMIT_STATUS 4
 
+enum signature_trust_level {
+       TRUST_UNDEFINED,
+       TRUST_NEVER,
+       TRUST_MARGINAL,
+       TRUST_FULLY,
+       TRUST_ULTIMATE,
+};
+
 struct signature_check {
        char *payload;
        char *gpg_output;
@@ -16,7 +24,6 @@ struct signature_check {
         * possible "result":
         * 0 (not checked)
         * N (checked but no further result)
-        * U (untrusted good)
         * G (good)
         * B (bad)
         */
@@ -25,6 +32,7 @@ struct signature_check {
        char *key;
        char *fingerprint;
        char *primary_key_fingerprint;
+       enum signature_trust_level trust_level;
 };
 
 void signature_check_clear(struct signature_check *sigc);
diff --git a/graph.c b/graph.c
index aaf97069bd0b140da425dbc1a5b9fc7250459f13..4fb25ad464db5778262936cdcc97b5088abc7678 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -1233,8 +1233,14 @@ static void graph_output_collapsing_line(struct git_graph *graph, struct graph_l
                         * prevent any other edges from moving
                         * horizontally.
                         */
-                       if (horizontal_edge == -1)
-                               horizontal_edge = i;
+                       if (horizontal_edge == -1) {
+                               int j;
+                               horizontal_edge_target = target;
+                               horizontal_edge = i - 1;
+
+                               for (j = (target * 2) + 3; j < (i - 2); j += 2)
+                                       graph->mapping[j] = target;
+                       }
                }
        }
 
index 55ee63935073e2b2790d82e52399aaa1638f9e8f..8c53c0d9bf9b80aec955c24b33a0a022bf43d5ff 100644 (file)
@@ -208,6 +208,14 @@ int hash_object_file_literally(const void *buf, unsigned long len,
                               const char *type, struct object_id *oid,
                               unsigned flags);
 
+/*
+ * Add an object file to the in-memory object store, without writing it
+ * to disk.
+ *
+ * Callers are responsible for calling write_object_file to record the
+ * object in persistent storage before writing any other new objects
+ * that reference it.
+ */
 int pretend_object_file(void *, unsigned long, enum object_type,
                        struct object_id *oid);
 
@@ -292,8 +300,6 @@ struct object_info {
 #define OBJECT_INFO_LOOKUP_REPLACE 1
 /* Allow reading from a loose object file of unknown/bogus type */
 #define OBJECT_INFO_ALLOW_UNKNOWN_TYPE 2
-/* Do not check cached storage */
-#define OBJECT_INFO_SKIP_CACHED 4
 /* Do not retry packed storage after checking packed and loose storage */
 #define OBJECT_INFO_QUICK 8
 /* Do not check loose object */
index 305e903192a7ae4fb125090436130f434bd46e7c..f5fbbc5ffb769e63312c80275dacf59ef28c9731 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1311,9 +1311,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                case '?':
                        switch (c->signature_check.result) {
                        case 'G':
+                               switch (c->signature_check.trust_level) {
+                               case TRUST_UNDEFINED:
+                               case TRUST_NEVER:
+                                       strbuf_addch(sb, 'U');
+                                       break;
+                               default:
+                                       strbuf_addch(sb, 'G');
+                                       break;
+                               }
+                               break;
                        case 'B':
                        case 'E':
-                       case 'U':
                        case 'N':
                        case 'X':
                        case 'Y':
@@ -1337,6 +1346,25 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        if (c->signature_check.primary_key_fingerprint)
                                strbuf_addstr(sb, c->signature_check.primary_key_fingerprint);
                        break;
+               case 'T':
+                       switch (c->signature_check.trust_level) {
+                       case TRUST_UNDEFINED:
+                               strbuf_addstr(sb, "undefined");
+                               break;
+                       case TRUST_NEVER:
+                               strbuf_addstr(sb, "never");
+                               break;
+                       case TRUST_MARGINAL:
+                               strbuf_addstr(sb, "marginal");
+                               break;
+                       case TRUST_FULLY:
+                               strbuf_addstr(sb, "fully");
+                               break;
+                       case TRUST_ULTIMATE:
+                               strbuf_addstr(sb, "ultimate");
+                               break;
+                       }
+                       break;
                default:
                        return 0;
                }
index 9942f120a9b928831008a561ce6f09b60fa5b0e0..f5e1149f9b395e77c3d55a147332c1117746d7b1 100644 (file)
@@ -213,8 +213,9 @@ static char *locate_in_PATH(const char *file)
 static int exists_in_PATH(const char *file)
 {
        char *r = locate_in_PATH(file);
+       int found = r != NULL;
        free(r);
-       return r != NULL;
+       return found;
 }
 
 int sane_execvp(const char *file, char * const argv[])
diff --git a/setup.c b/setup.c
index e2a479a64fa4076bad8eaf6cf0949d0ae263da9d..12228c0d9c1e3dfb91c474aba8e891f0836dc6d9 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -197,9 +197,26 @@ static void NORETURN die_verify_filename(struct repository *r,
  */
 static int looks_like_pathspec(const char *arg)
 {
-       /* anything with a wildcard character */
-       if (!no_wildcard(arg))
-               return 1;
+       const char *p;
+       int escaped = 0;
+
+       /*
+        * Wildcard characters imply the user is looking to match pathspecs
+        * that aren't in the filesystem. Note that this doesn't include
+        * backslash even though it's a glob special; by itself it doesn't
+        * cause any increase in the match. Likewise ignore backslash-escaped
+        * wildcard characters.
+        */
+       for (p = arg; *p; p++) {
+               if (escaped) {
+                       escaped = 0;
+               } else if (is_glob_special(*p)) {
+                       if (*p == '\\')
+                               escaped = 1;
+                       else
+                               return 1;
+               }
+       }
 
        /* long-form pathspec magic */
        if (starts_with(arg, ":("))
index 188de57634bb1b0b15fbe3ab97f062bed7927763..03ae9ae93a53d911137a8f06aa687472c73806c9 100644 (file)
@@ -1417,6 +1417,7 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid,
                             struct object_info *oi, unsigned flags)
 {
        static struct object_info blank_oi = OBJECT_INFO_INIT;
+       struct cached_object *co;
        struct pack_entry e;
        int rtype;
        const struct object_id *real = oid;
@@ -1431,24 +1432,22 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid,
        if (!oi)
                oi = &blank_oi;
 
-       if (!(flags & OBJECT_INFO_SKIP_CACHED)) {
-               struct cached_object *co = find_cached_object(real);
-               if (co) {
-                       if (oi->typep)
-                               *(oi->typep) = co->type;
-                       if (oi->sizep)
-                               *(oi->sizep) = co->size;
-                       if (oi->disk_sizep)
-                               *(oi->disk_sizep) = 0;
-                       if (oi->delta_base_sha1)
-                               hashclr(oi->delta_base_sha1);
-                       if (oi->type_name)
-                               strbuf_addstr(oi->type_name, type_name(co->type));
-                       if (oi->contentp)
-                               *oi->contentp = xmemdupz(co->buf, co->size);
-                       oi->whence = OI_CACHED;
-                       return 0;
-               }
+       co = find_cached_object(real);
+       if (co) {
+               if (oi->typep)
+                       *(oi->typep) = co->type;
+               if (oi->sizep)
+                       *(oi->sizep) = co->size;
+               if (oi->disk_sizep)
+                       *(oi->disk_sizep) = 0;
+               if (oi->delta_base_sha1)
+                       hashclr(oi->delta_base_sha1);
+               if (oi->type_name)
+                       strbuf_addstr(oi->type_name, type_name(co->type));
+               if (oi->contentp)
+                       *oi->contentp = xmemdupz(co->buf, co->size);
+               oi->whence = OI_CACHED;
+               return 0;
        }
 
        while (1) {
@@ -1932,8 +1931,7 @@ int repo_has_object_file_with_flags(struct repository *r,
 {
        if (!startup_info->have_repository)
                return 0;
-       return oid_object_info_extended(r, oid, NULL,
-                                       flags | OBJECT_INFO_SKIP_CACHED) >= 0;
+       return oid_object_info_extended(r, oid, NULL, flags) >= 0;
 }
 
 int repo_has_object_file(struct repository *r,
index 7bb0ad07e61774b595b6b448a15a5d36a5cc8877..6c5d274126ac6c5e1b796f0a33fb33fba3ebda2a 100644 (file)
@@ -4,7 +4,8 @@
 /**
  * The string_list API offers a data structure and functions to handle
  * sorted and unsorted arrays of strings.  A "sorted" list is one whose
- * entries are sorted by string value in `strcmp()` order.
+ * entries are sorted by string value in the order specified by the `cmp`
+ * member (`strcmp()` by default).
  *
  * The caller:
  *
@@ -209,7 +210,8 @@ struct string_list_item *string_list_append(struct string_list *list, const char
 struct string_list_item *string_list_append_nodup(struct string_list *list, char *string);
 
 /**
- * Sort the list's entries by string value in `strcmp()` order.
+ * Sort the list's entries by string value in order specified by list->cmp
+ * (strcmp() if list->cmp is NULL).
  */
 void string_list_sort(struct string_list *list);
 
index 9da7181321f089e8450ec7e39692f7928f5638a8..5e8e3d14802adc1baad0071df07d70bcdc57d371 100644 (file)
@@ -82,7 +82,7 @@ int is_staging_gitmodules_ok(struct index_state *istate)
        if ((pos >= 0) && (pos < istate->cache_nr)) {
                struct stat st;
                if (lstat(GITMODULES_FILE, &st) == 0 &&
-                   ie_match_stat(istate, istate->cache[pos], &st, 0) & DATA_CHANGED)
+                   ie_modified(istate, istate->cache[pos], &st, 0) & DATA_CHANGED)
                        return 0;
        }
 
@@ -431,7 +431,7 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,
        else if (!strcmp(arg, "dirty"))
                diffopt->flags.ignore_dirty_submodules = 1;
        else if (strcmp(arg, "none"))
-               die("bad --ignore-submodules argument: %s", arg);
+               die(_("bad --ignore-submodules argument: %s"), arg);
        /*
         * Please update _git_status() in git-completion.bash when you
         * add new options
@@ -812,9 +812,9 @@ static void collect_changed_submodules_cb(struct diff_queue_struct *q,
                                submodule = submodule_from_name(me->repo,
                                                                commit_oid, name);
                        if (submodule) {
-                               warning("Submodule in commit %s at path: "
+                               warning(_("Submodule in commit %s at path: "
                                        "'%s' collides with a submodule named "
-                                       "the same. Skipping it.",
+                                       "the same. Skipping it."),
                                        oid_to_hex(commit_oid), p->two->path);
                                name = NULL;
                        }
@@ -844,7 +844,7 @@ static void collect_changed_submodules(struct repository *r,
        repo_init_revisions(r, &rev, NULL);
        setup_revisions(argv->argc, argv->argv, &rev, NULL);
        if (prepare_revision_walk(&rev))
-               die("revision walk setup failed");
+               die(_("revision walk setup failed"));
 
        while ((commit = get_revision(&rev))) {
                struct rev_info diff_rev;
@@ -992,7 +992,7 @@ static int submodule_needs_pushing(struct repository *r,
                cp.out = -1;
                cp.dir = path;
                if (start_command(&cp))
-                       die("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s",
+                       die(_("Could not run 'git rev-list <commits> --not --remotes -n 1' command in submodule %s"),
                                        path);
                if (strbuf_read(&buf, cp.out, the_hash_algo->hexsz + 1))
                        needs_pushing = 1;
@@ -1115,7 +1115,7 @@ static void submodule_push_check(const char *path, const char *head,
         * child process.
         */
        if (run_command(&cp))
-               die("process for submodule '%s' failed", path);
+               die(_("process for submodule '%s' failed"), path);
 }
 
 int push_unpushed_submodules(struct repository *r,
@@ -1155,10 +1155,10 @@ int push_unpushed_submodules(struct repository *r,
        /* Actually push the submodules */
        for (i = 0; i < needs_pushing.nr; i++) {
                const char *path = needs_pushing.items[i].string;
-               fprintf(stderr, "Pushing submodule '%s'\n", path);
+               fprintf(stderr, _("Pushing submodule '%s'\n"), path);
                if (!push_submodule(path, remote, rs,
                                    push_options, dry_run)) {
-                       fprintf(stderr, "Unable to push submodule '%s'\n", path);
+                       fprintf(stderr, _("Unable to push submodule '%s'\n"), path);
                        ret = 0;
                }
        }
@@ -1280,10 +1280,12 @@ struct submodule_parallel_fetch {
        /* Pending fetches by OIDs */
        struct fetch_task **oid_fetch_tasks;
        int oid_fetch_tasks_nr, oid_fetch_tasks_alloc;
+
+       struct strbuf submodules_with_errors;
 };
 #define SPF_INIT {0, ARGV_ARRAY_INIT, NULL, NULL, 0, 0, 0, 0, \
                  STRING_LIST_INIT_DUP, \
-                 NULL, 0, 0}
+                 NULL, 0, 0, STRBUF_INIT}
 
 static int get_fetch_recurse_config(const struct submodule *submodule,
                                    struct submodule_parallel_fetch *spf)
@@ -1448,7 +1450,7 @@ static int get_next_submodule(struct child_process *cp,
                        prepare_submodule_repo_env_in_gitdir(&cp->env_array);
                        cp->git_cmd = 1;
                        if (!spf->quiet)
-                               strbuf_addf(err, "Fetching submodule %s%s\n",
+                               strbuf_addf(err, _("Fetching submodule %s%s\n"),
                                            spf->prefix, ce->name);
                        argv_array_init(&cp->args);
                        argv_array_pushv(&cp->args, spf->args.argv);
@@ -1547,7 +1549,10 @@ static int fetch_finish(int retvalue, struct strbuf *err,
        struct string_list_item *it;
        struct oid_array *commits;
 
-       if (retvalue)
+       if (!task || !task->sub)
+               BUG("callback cookie bogus");
+
+       if (retvalue) {
                /*
                 * NEEDSWORK: This indicates that the overall fetch
                 * failed, even though there may be a subsequent fetch
@@ -1557,8 +1562,9 @@ static int fetch_finish(int retvalue, struct strbuf *err,
                 */
                spf->result = 1;
 
-       if (!task || !task->sub)
-               BUG("callback cookie bogus");
+               strbuf_addf(&spf->submodules_with_errors, "\t%s\n",
+                           task->sub->name);
+       }
 
        /* Is this the second time we process this submodule? */
        if (task->commits)
@@ -1610,7 +1616,7 @@ int fetch_populated_submodules(struct repository *r,
                goto out;
 
        if (repo_read_index(r) < 0)
-               die("index file corrupt");
+               die(_("index file corrupt"));
 
        argv_array_push(&spf.args, "fetch");
        for (i = 0; i < options->argc; i++)
@@ -1627,6 +1633,11 @@ int fetch_populated_submodules(struct repository *r,
                                   &spf,
                                   "submodule", "parallel/fetch");
 
+       if (spf.submodules_with_errors.len > 0)
+               fprintf(stderr, _("Errors during submodule fetch:\n%s"),
+                       spf.submodules_with_errors.buf);
+
+
        argv_array_clear(&spf.args);
 out:
        free_submodules_oids(&spf.changed_submodule_names);
@@ -1665,7 +1676,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        cp.out = -1;
        cp.dir = path;
        if (start_command(&cp))
-               die("Could not run 'git status --porcelain=2' in submodule %s", path);
+               die(_("Could not run 'git status --porcelain=2' in submodule %s"), path);
 
        fp = xfdopen(cp.out, "r");
        while (strbuf_getwholeline(&buf, fp, '\n') != EOF) {
@@ -1706,7 +1717,7 @@ unsigned is_submodule_modified(const char *path, int ignore_untracked)
        fclose(fp);
 
        if (finish_command(&cp) && !ignore_cp_exit_code)
-               die("'git status --porcelain=2' failed in submodule %s", path);
+               die(_("'git status --porcelain=2' failed in submodule %s"), path);
 
        strbuf_release(&buf);
        return dirty_submodule;
@@ -1811,7 +1822,7 @@ out:
 void submodule_unset_core_worktree(const struct submodule *sub)
 {
        char *config_path = xstrfmt("%s/modules/%s/config",
-                                   get_git_common_dir(), sub->name);
+                                   get_git_dir(), sub->name);
 
        if (git_config_set_in_file_gently(config_path, "core.worktree", NULL))
                warning(_("Could not unset core.worktree setting in submodule '%s'"),
@@ -1841,7 +1852,7 @@ static int submodule_has_dirty_index(const struct submodule *sub)
        cp.no_stdout = 1;
        cp.dir = sub->path;
        if (start_command(&cp))
-               die("could not recurse into submodule '%s'", sub->path);
+               die(_("could not recurse into submodule '%s'"), sub->path);
 
        return finish_command(&cp);
 }
@@ -1862,7 +1873,7 @@ static void submodule_reset_index(const char *path)
        argv_array_push(&cp.args, empty_tree_oid_hex());
 
        if (run_command(&cp))
-               die("could not reset submodule index");
+               die(_("could not reset submodule index"));
 }
 
 /**
@@ -1914,7 +1925,7 @@ int submodule_move_head(const char *path,
                                        ABSORB_GITDIR_RECURSE_SUBMODULES);
                } else {
                        char *gitdir = xstrfmt("%s/modules/%s",
-                                   get_git_common_dir(), sub->name);
+                                   get_git_dir(), sub->name);
                        connect_work_tree_and_git_dir(path, gitdir, 0);
                        free(gitdir);
 
@@ -1924,7 +1935,7 @@ int submodule_move_head(const char *path,
 
                if (old_head && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
                        char *gitdir = xstrfmt("%s/modules/%s",
-                                   get_git_common_dir(), sub->name);
+                                   get_git_dir(), sub->name);
                        connect_work_tree_and_git_dir(path, gitdir, 1);
                        free(gitdir);
                }
index 38bfeebd881ae8930d965536a42305771b066477..fd3303552bec0d3ca243845634179eb35890866a 100755 (executable)
@@ -46,7 +46,7 @@ while (<>) {
        /(?:\$\(seq|^\s*seq\b)/ and err 'seq is not portable (use test_seq)';
        /\bgrep\b.*--file\b/ and err 'grep --file FILE is not portable (use grep -f FILE)';
        /\bexport\s+[A-Za-z0-9_]*=/ and err '"export FOO=bar" is not portable (use FOO=bar && export FOO)';
-       /^\s*([A-Z0-9_]+=(\w+|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
+       /^\s*([A-Z0-9_]+=(\w*|(["']).*?\3)\s+)+(\w+)/ and exists($func{$4}) and
                err '"FOO=bar shell_func" assignment extends beyond "shell_func"';
        $line = '';
        # this resets our $. for each file
diff --git a/t/helper/test-parse-pathspec-file.c b/t/helper/test-parse-pathspec-file.c
new file mode 100644 (file)
index 0000000..02f4ccf
--- /dev/null
@@ -0,0 +1,33 @@
+#include "test-tool.h"
+#include "parse-options.h"
+#include "pathspec.h"
+#include "gettext.h"
+
+int cmd__parse_pathspec_file(int argc, const char **argv)
+{
+       struct pathspec pathspec;
+       const char *pathspec_from_file = 0;
+       int pathspec_file_nul = 0, i;
+
+       static const char *const usage[] = {
+               "test-tool parse-pathspec-file --pathspec-from-file [--pathspec-file-nul]",
+               NULL
+       };
+
+       struct option options[] = {
+               OPT_PATHSPEC_FROM_FILE(&pathspec_from_file),
+               OPT_PATHSPEC_FILE_NUL(&pathspec_file_nul),
+               OPT_END()
+       };
+
+       parse_options(argc, argv, 0, options, usage, 0);
+
+       parse_pathspec_file(&pathspec, 0, 0, 0, pathspec_from_file,
+                           pathspec_file_nul);
+
+       for (i = 0; i < pathspec.nr; i++)
+               printf("%s\n", pathspec.items[i].original);
+
+       clear_pathspec(&pathspec);
+       return 0;
+}
index f20989d4497b596685abfb72bad1f048e41b212c..c9a232d23897b70b051bfd76488ca0e971830b83 100644 (file)
@@ -39,6 +39,7 @@ static struct test_cmd cmds[] = {
        { "oidmap", cmd__oidmap },
        { "online-cpus", cmd__online_cpus },
        { "parse-options", cmd__parse_options },
+       { "parse-pathspec-file", cmd__parse_pathspec_file },
        { "path-utils", cmd__path_utils },
        { "pkt-line", cmd__pkt_line },
        { "prio-queue", cmd__prio_queue },
index 8ed2af71d1b2382def4215862a6b5585876a62cc..c8549fd87f23b6372a41b2ad5d7ba28df3e258c7 100644 (file)
@@ -29,6 +29,7 @@ int cmd__mktemp(int argc, const char **argv);
 int cmd__oidmap(int argc, const char **argv);
 int cmd__online_cpus(int argc, const char **argv);
 int cmd__parse_options(int argc, const char **argv);
+int cmd__parse_pathspec_file(int argc, const char** argv);
 int cmd__path_utils(int argc, const char **argv);
 int cmd__pkt_line(int argc, const char **argv);
 int cmd__prio_queue(int argc, const char **argv);
index 547b9f88e1235a9248f6da9d1fa24dffd7cb0cd3..5aff2abe8b5490156e7745beb5ad01d9cacc0d58 100644 (file)
@@ -175,7 +175,7 @@ stop_and_cleanup_p4d () {
 
 cleanup_git () {
        retry_until_success rm -r "$git"
-       test_must_fail test -d "$git" &&
+       test_path_is_missing "$git" &&
        retry_until_success mkdir "$git"
 }
 
index 8a81a249d0b49638c28e066a2bd757f3ef3f699d..3e440c078d5752620a9ce9dc91f61e9441cb3665 100755 (executable)
@@ -155,7 +155,7 @@ test_expect_success 'pretend we have a fully passing test suite' "
 "
 
 test_expect_success 'pretend we have a partially passing test suite' "
-       test_must_fail run_sub_test_lib_test \
+       run_sub_test_lib_test_err \
                partial-pass '2/3 tests passing' <<-\\EOF &&
        test_expect_success 'passing test #1' 'true'
        test_expect_success 'failing test #2' 'false'
@@ -219,7 +219,7 @@ test_expect_success 'pretend we have fixed one of two known breakages (run in su
 "
 
 test_expect_success 'pretend we have a pass, fail, and known breakage' "
-       test_must_fail run_sub_test_lib_test \
+       run_sub_test_lib_test_err \
                mixed-results1 'mixed results #1' <<-\\EOF &&
        test_expect_success 'passing test' 'true'
        test_expect_success 'failing test' 'false'
@@ -238,7 +238,7 @@ test_expect_success 'pretend we have a pass, fail, and known breakage' "
 "
 
 test_expect_success 'pretend we have a mix of all possible results' "
-       test_must_fail run_sub_test_lib_test \
+       run_sub_test_lib_test_err \
                mixed-results2 'mixed results #2' <<-\\EOF &&
        test_expect_success 'passing test' 'true'
        test_expect_success 'passing test' 'true'
@@ -274,7 +274,7 @@ test_expect_success 'pretend we have a mix of all possible results' "
 "
 
 test_expect_success C_LOCALE_OUTPUT 'test --verbose' '
-       test_must_fail run_sub_test_lib_test \
+       run_sub_test_lib_test_err \
                t1234-verbose "test verbose" --verbose <<-\EOF &&
        test_expect_success "passing test" true
        test_expect_success "test with output" "echo foo"
@@ -301,7 +301,7 @@ test_expect_success C_LOCALE_OUTPUT 'test --verbose' '
 '
 
 test_expect_success 'test --verbose-only' '
-       test_must_fail run_sub_test_lib_test \
+       run_sub_test_lib_test_err \
                t2345-verbose-only-2 "test verbose-only=2" \
                --verbose-only=2 <<-\EOF &&
        test_expect_success "passing test" true
@@ -834,7 +834,7 @@ then
 fi
 
 test_expect_success 'tests clean up even on failures' "
-       test_must_fail run_sub_test_lib_test \
+       run_sub_test_lib_test_err \
                failing-cleanup 'Failing tests with cleanup commands' <<-\\EOF &&
        test_expect_success 'tests clean up even after a failure' '
                touch clean-after-failure &&
@@ -863,7 +863,7 @@ test_expect_success 'tests clean up even on failures' "
 "
 
 test_expect_success 'test_atexit is run' "
-       test_must_fail run_sub_test_lib_test \
+       run_sub_test_lib_test_err \
                atexit-cleanup 'Run atexit commands' -i <<-\\EOF &&
        test_expect_success 'tests clean up even after a failure' '
                > ../../clean-atexit &&
index 71e63d8b509d34fa4b860bac9ba1531e674799d7..b660593c20f8d4012e8c81e2276004708c2a1259 100755 (executable)
@@ -5,19 +5,16 @@ test_description=gitattributes
 . ./test-lib.sh
 
 attr_check () {
-       path="$1" expect="$2"
+       path="$1" expect="$2" git_opts="$3" &&
 
-       git $3 check-attr test -- "$path" >actual 2>err &&
-       echo "$path: test: $2" >expect &&
+       git $git_opts check-attr test -- "$path" >actual 2>err &&
+       echo "$path: test: $expect" >expect &&
        test_cmp expect actual &&
-       test_line_count = 0 err
+       test_must_be_empty err
 }
 
 attr_check_quote () {
-
-       path="$1"
-       quoted_path="$2"
-       expect="$3"
+       path="$1" quoted_path="$2" expect="$3" &&
 
        git check-attr test -- "$path" >actual &&
        echo "\"$quoted_path\": test: $expect" >expect &&
@@ -27,7 +24,7 @@ attr_check_quote () {
 
 test_expect_success 'open-quoted pathname' '
        echo "\"a test=a" >.gitattributes &&
-       test_must_fail attr_check a a
+       attr_check a unspecified
 '
 
 
@@ -112,20 +109,20 @@ test_expect_success 'attribute test' '
 
 test_expect_success 'attribute matching is case sensitive when core.ignorecase=0' '
 
-       test_must_fail attr_check F f "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/F f "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/c/F f "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/G a/g "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/B/g a/b/g "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/b/G a/b/g "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/b/H a/b/h "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/b/D/g "a/b/d/*" "-c core.ignorecase=0" &&
-       test_must_fail attr_check oNoFf unset "-c core.ignorecase=0" &&
-       test_must_fail attr_check oFfOn set "-c core.ignorecase=0" &&
+       attr_check F unspecified "-c core.ignorecase=0" &&
+       attr_check a/F unspecified "-c core.ignorecase=0" &&
+       attr_check a/c/F unspecified "-c core.ignorecase=0" &&
+       attr_check a/G unspecified "-c core.ignorecase=0" &&
+       attr_check a/B/g a/g "-c core.ignorecase=0" &&
+       attr_check a/b/G unspecified "-c core.ignorecase=0" &&
+       attr_check a/b/H unspecified "-c core.ignorecase=0" &&
+       attr_check a/b/D/g a/g "-c core.ignorecase=0" &&
+       attr_check oNoFf unspecified "-c core.ignorecase=0" &&
+       attr_check oFfOn unspecified "-c core.ignorecase=0" &&
        attr_check NO unspecified "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/b/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+       attr_check a/b/D/NO unspecified "-c core.ignorecase=0" &&
        attr_check a/b/d/YES a/b/d/* "-c core.ignorecase=0" &&
-       test_must_fail attr_check a/E/f "A/e/F" "-c core.ignorecase=0"
+       attr_check a/E/f f "-c core.ignorecase=0"
 
 '
 
@@ -149,8 +146,8 @@ test_expect_success 'attribute matching is case insensitive when core.ignorecase
 '
 
 test_expect_success CASE_INSENSITIVE_FS 'additional case insensitivity tests' '
-       test_must_fail attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=0" &&
-       test_must_fail attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=0" &&
+       attr_check a/B/D/g a/g "-c core.ignorecase=0" &&
+       attr_check A/B/D/NO unspecified "-c core.ignorecase=0" &&
        attr_check A/b/h a/b/h "-c core.ignorecase=1" &&
        attr_check a/B/D/g "a/b/d/*" "-c core.ignorecase=1" &&
        attr_check A/B/D/NO "a/b/d/*" "-c core.ignorecase=1"
@@ -244,7 +241,7 @@ EOF
        git check-attr foo -- "a/b/f" >>actual 2>>err &&
        git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
        test_cmp expect actual &&
-       test_line_count = 0 err
+       test_must_be_empty err
 '
 
 test_expect_success '"**" with no slashes test' '
@@ -265,7 +262,7 @@ EOF
        git check-attr foo -- "a/b/f" >>actual 2>>err &&
        git check-attr foo -- "a/b/c/f" >>actual 2>>err &&
        test_cmp expect actual &&
-       test_line_count = 0 err
+       test_must_be_empty err
 '
 
 test_expect_success 'using --git-dir and --work-tree' '
index 854da0ae16f8c437c239a0a4e16f1549d87521a2..b63ba62e5db4760813b04c26414c497b8aadbfbe 100755 (executable)
@@ -159,8 +159,8 @@ test_expect_success 'checkout with autocrlf=input' '
        rm -f tmp one dir/two three &&
        git config core.autocrlf input &&
        git read-tree --reset -u HEAD &&
-       test_must_fail has_cr one &&
-       test_must_fail has_cr dir/two &&
+       ! has_cr one &&
+       ! has_cr dir/two &&
        git update-index -- one dir/two &&
        test "$one" = $(git hash-object --stdin <one) &&
        test "$two" = $(git hash-object --stdin <dir/two) &&
@@ -237,9 +237,9 @@ test_expect_success '.gitattributes says two is binary' '
        git config core.autocrlf true &&
        git read-tree --reset -u HEAD &&
 
-       test_must_fail has_cr dir/two &&
+       ! has_cr dir/two &&
        verbose has_cr one &&
-       test_must_fail has_cr three
+       ! has_cr three
 '
 
 test_expect_success '.gitattributes says two is input' '
@@ -248,7 +248,7 @@ test_expect_success '.gitattributes says two is input' '
        echo "two crlf=input" >.gitattributes &&
        git read-tree --reset -u HEAD &&
 
-       test_must_fail has_cr dir/two
+       ! has_cr dir/two
 '
 
 test_expect_success '.gitattributes says two and three are text' '
@@ -270,7 +270,7 @@ test_expect_success 'in-tree .gitattributes (1)' '
        rm -rf tmp one dir .gitattributes patch.file three &&
        git read-tree --reset -u HEAD &&
 
-       test_must_fail has_cr one &&
+       ! has_cr one &&
        verbose has_cr three
 '
 
@@ -280,7 +280,7 @@ test_expect_success 'in-tree .gitattributes (2)' '
        git read-tree --reset HEAD &&
        git checkout-index -f -q -u -a &&
 
-       test_must_fail has_cr one &&
+       ! has_cr one &&
        verbose has_cr three
 '
 
@@ -291,7 +291,7 @@ test_expect_success 'in-tree .gitattributes (3)' '
        git checkout-index -u .gitattributes &&
        git checkout-index -u one dir/two three &&
 
-       test_must_fail has_cr one &&
+       ! has_cr one &&
        verbose has_cr three
 '
 
@@ -302,7 +302,7 @@ test_expect_success 'in-tree .gitattributes (4)' '
        git checkout-index -u one dir/two three &&
        git checkout-index -u .gitattributes &&
 
-       test_must_fail has_cr one &&
+       ! has_cr one &&
        verbose has_cr three
 '
 
diff --git a/t/t0067-parse_pathspec_file.sh b/t/t0067-parse_pathspec_file.sh
new file mode 100755 (executable)
index 0000000..7bab49f
--- /dev/null
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+test_description='Test parse_pathspec_file()'
+
+. ./test-lib.sh
+
+test_expect_success 'one item from stdin' '
+       cat >expect <<-\EOF &&
+       fileA.t
+       EOF
+
+       echo fileA.t |
+       test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'one item from file' '
+       cat >expect <<-\EOF &&
+       fileA.t
+       EOF
+
+       echo fileA.t >list &&
+       test-tool parse-pathspec-file --pathspec-from-file=list >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'NUL delimiters' '
+       cat >expect <<-\EOF &&
+       fileA.t
+       fileB.t
+       EOF
+
+       printf "fileA.t\0fileB.t\0" |
+       test-tool parse-pathspec-file --pathspec-from-file=- --pathspec-file-nul >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'LF delimiters' '
+       cat >expect <<-\EOF &&
+       fileA.t
+       fileB.t
+       EOF
+
+       printf "fileA.t\nfileB.t\n" |
+       test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'no trailing delimiter' '
+       cat >expect <<-\EOF &&
+       fileA.t
+       fileB.t
+       EOF
+
+       printf "fileA.t\nfileB.t" |
+       test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'CRLF delimiters' '
+       cat >expect <<-\EOF &&
+       fileA.t
+       fileB.t
+       EOF
+
+       printf "fileA.t\r\nfileB.t\r\n" |
+       test-tool parse-pathspec-file --pathspec-from-file=- >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success 'quotes' '
+       cat >expect <<-\EOF &&
+       fileA.t
+       EOF
+
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
+       test-tool parse-pathspec-file --pathspec-from-file=list >actual &&
+
+       test_cmp expect actual
+'
+
+test_expect_success '--pathspec-file-nul takes quotes literally' '
+       # Note: there is an extra newline because --pathspec-file-nul takes
+       # input \n literally, too
+       cat >expect <<-\EOF &&
+       "file\101.t"
+
+       EOF
+
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
+       test-tool parse-pathspec-file --pathspec-from-file=list --pathspec-file-nul >actual &&
+
+       test_cmp expect actual
+'
+
+test_done
index 21e139a313bed1d91121044f64a5e7ce5078002c..dd87b43be1a6b3331a2ddee4b7ad87ab1c9041bd 100755 (executable)
@@ -153,7 +153,7 @@ test_expect_success 'Checking attributes in both XDG and local attributes files'
 
 
 test_expect_success 'Checking attributes in a non-XDG global attributes file' '
-       test_might_fail rm .gitattributes &&
+       rm -f .gitattributes &&
        echo "f attr_f=test" >"$HOME"/my_gitattributes &&
        git config core.attributesfile "$HOME"/my_gitattributes &&
        echo "f: attr_f: test" >expected &&
@@ -165,7 +165,7 @@ test_expect_success 'Checking attributes in a non-XDG global attributes file' '
 test_expect_success 'write: xdg file exists and ~/.gitconfig doesn'\''t' '
        mkdir -p "$HOME"/.config/git &&
        >"$HOME"/.config/git/config &&
-       test_might_fail rm "$HOME"/.gitconfig &&
+       rm -f "$HOME"/.gitconfig &&
        git config --global user.name "write_config" &&
        echo "[user]" >expected &&
        echo "  name = write_config" >>expected &&
@@ -183,8 +183,8 @@ test_expect_success 'write: xdg file exists and ~/.gitconfig exists' '
 
 
 test_expect_success 'write: ~/.config/git/ exists and config file doesn'\''t' '
-       test_might_fail rm "$HOME"/.gitconfig &&
-       test_might_fail rm "$HOME"/.config/git/config &&
+       rm -f "$HOME"/.gitconfig &&
+       rm -f "$HOME"/.config/git/config &&
        git config --global user.name "write_gitconfig" &&
        echo "[user]" >expected &&
        echo "  name = write_gitconfig" >>expected &&
index 37dc689d8c98fd776895e1ce6d18faee092001d3..002e6d3388e242c56800da6b9640819c90ed5615 100755 (executable)
@@ -74,7 +74,7 @@ test_expect_success 'can parse blob ending with CR' '
 '
 
 test_expect_success 'config --blob outside of a repository is an error' '
-       test_must_fail nongit git config --blob=foo --list
+       nongit test_must_fail git config --blob=foo --list
 '
 
 test_done
index e5cb8a252dd1a7d8e65eece2fe4ee5e21d748b90..be12fb63506e2a8c242e876526dbe1d60e105f49 100755 (executable)
@@ -8,7 +8,7 @@ test_description='avoid rewriting packed-refs unnecessarily'
 # shouldn't upset readers, and it should be omitted if the file is
 # ever rewritten.
 mark_packed_refs () {
-       sed -e "s/^\(#.*\)/\1 t1409 /" <.git/packed-refs >.git/packed-refs.new &&
+       sed -e "s/^\(#.*\)/\1 t1409 /" .git/packed-refs >.git/packed-refs.new &&
        mv .git/packed-refs.new .git/packed-refs
 }
 
@@ -27,15 +27,15 @@ test_expect_success 'setup' '
 '
 
 test_expect_success 'do not create packed-refs file gratuitously' '
-       test_must_fail test -f .git/packed-refs &&
+       test_path_is_missing .git/packed-refs &&
        git update-ref refs/heads/foo $A &&
-       test_must_fail test -f .git/packed-refs &&
+       test_path_is_missing .git/packed-refs &&
        git update-ref refs/heads/foo $B &&
-       test_must_fail test -f .git/packed-refs &&
+       test_path_is_missing .git/packed-refs &&
        git update-ref refs/heads/foo $C $B &&
-       test_must_fail test -f .git/packed-refs &&
+       test_path_is_missing .git/packed-refs &&
        git update-ref -d refs/heads/foo &&
-       test_must_fail test -f .git/packed-refs
+       test_path_is_missing .git/packed-refs
 '
 
 test_expect_success 'check that marking the packed-refs file works' '
@@ -46,7 +46,7 @@ test_expect_success 'check that marking the packed-refs file works' '
        git for-each-ref >actual &&
        test_cmp expected actual &&
        git pack-refs --all &&
-       test_must_fail check_packed_refs_marked &&
+       ! check_packed_refs_marked &&
        git for-each-ref >actual2 &&
        test_cmp expected actual2
 '
@@ -80,7 +80,7 @@ test_expect_success 'touch packed-refs on delete of packed' '
        git pack-refs --all &&
        mark_packed_refs &&
        git update-ref -d refs/heads/packed-delete &&
-       test_must_fail check_packed_refs_marked
+       ! check_packed_refs_marked
 '
 
 test_expect_success 'leave packed-refs untouched on update of loose' '
index 3498d3d55e9e18d19c9a9accaa1637f338c1c4bc..b75558040ffd6ee9174ed37bd2ca746212b57013 100755 (executable)
@@ -350,7 +350,7 @@ test_expect_success 'Multi-worktree setup' '
        mkdir work &&
        mkdir -p repo.git/repos/foo &&
        cp repo.git/HEAD repo.git/index repo.git/repos/foo &&
-       test_might_fail cp repo.git/sharedindex.* repo.git/repos/foo &&
+       { cp repo.git/sharedindex.* repo.git/repos/foo || :; } &&
        sane_unset GIT_DIR GIT_CONFIG GIT_WORK_TREE
 '
 
index 6d951ca015912062fba4a938fdb560c10641ca6d..8a75f37a113c82f7f7abc41d2dad7930f85172e8 100755 (executable)
@@ -222,4 +222,18 @@ test_expect_success 'reject Nth ancestor if N is too high' '
        test_must_fail git rev-parse HEAD~100000000000000000000000000000000
 '
 
+test_expect_success 'pathspecs with wildcards are not ambiguous' '
+       echo "*.c" >expect &&
+       git rev-parse "*.c" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'backslash does not trigger wildcard rule' '
+       test_must_fail git rev-parse "foo\\bar"
+'
+
+test_expect_success 'escaped char does not trigger wildcard rule' '
+       test_must_fail git rev-parse "foo\\*bar"
+'
+
 test_done
index 8b4cf8a6e3c2c9363210cd7a2bd619acd9e1c0b7..dfc0d96d8a8a834743781a08a8ea1c5f9dd63bda 100755 (executable)
@@ -28,14 +28,9 @@ test_expect_success 'setup' '
        )
 '
 
-full_name () {
-       (cd clone &&
-        git rev-parse --symbolic-full-name "$@")
-}
-
 commit_subject () {
        (cd clone &&
-        git show -s --pretty=format:%s "$@")
+        git show -s --pretty=tformat:%s "$@")
 }
 
 error_message () {
@@ -44,63 +39,78 @@ error_message () {
 }
 
 test_expect_success '@{upstream} resolves to correct full name' '
-       test refs/remotes/origin/master = "$(full_name @{upstream})" &&
-       test refs/remotes/origin/master = "$(full_name @{UPSTREAM})" &&
-       test refs/remotes/origin/master = "$(full_name @{UpSTReam})"
+       echo refs/remotes/origin/master >expect &&
+       git -C clone rev-parse --symbolic-full-name @{upstream} >actual &&
+       test_cmp expect actual &&
+       git -C clone rev-parse --symbolic-full-name @{UPSTREAM} >actual &&
+       test_cmp expect actual &&
+       git -C clone rev-parse --symbolic-full-name @{UpSTReam} >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '@{u} resolves to correct full name' '
-       test refs/remotes/origin/master = "$(full_name @{u})" &&
-       test refs/remotes/origin/master = "$(full_name @{U})"
+       echo refs/remotes/origin/master >expect &&
+       git -C clone rev-parse --symbolic-full-name @{u} >actual &&
+       test_cmp expect actual &&
+       git -C clone rev-parse --symbolic-full-name @{U} >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'my-side@{upstream} resolves to correct full name' '
-       test refs/remotes/origin/side = "$(full_name my-side@{u})"
+       echo refs/remotes/origin/side >expect &&
+       git -C clone rev-parse --symbolic-full-name my-side@{u} >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'upstream of branch with @ in middle' '
-       full_name fun@ny@{u} >actual &&
+       git -C clone rev-parse --symbolic-full-name fun@ny@{u} >actual &&
        echo refs/remotes/origin/side >expect &&
        test_cmp expect actual &&
-       full_name fun@ny@{U} >actual &&
+       git -C clone rev-parse --symbolic-full-name fun@ny@{U} >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'upstream of branch with @ at start' '
-       full_name @funny@{u} >actual &&
+       git -C clone rev-parse --symbolic-full-name @funny@{u} >actual &&
        echo refs/remotes/origin/side >expect &&
        test_cmp expect actual
 '
 
 test_expect_success 'upstream of branch with @ at end' '
-       full_name funny@@{u} >actual &&
+       git -C clone rev-parse --symbolic-full-name funny@@{u} >actual &&
        echo refs/remotes/origin/side >expect &&
        test_cmp expect actual
 '
 
 test_expect_success 'refs/heads/my-side@{upstream} does not resolve to my-side{upstream}' '
-       test_must_fail full_name refs/heads/my-side@{upstream}
+       test_must_fail git -C clone rev-parse --symbolic-full-name refs/heads/my-side@{upstream}
 '
 
 test_expect_success 'my-side@{u} resolves to correct commit' '
        git checkout side &&
        test_commit 5 &&
        (cd clone && git fetch) &&
-       test 2 = "$(commit_subject my-side)" &&
-       test 5 = "$(commit_subject my-side@{u})"
+       echo 2 >expect &&
+       commit_subject my-side >actual &&
+       test_cmp expect actual &&
+       echo 5 >expect &&
+       commit_subject my-side@{u} >actual
 '
 
 test_expect_success 'not-tracking@{u} fails' '
-       test_must_fail full_name non-tracking@{u} &&
+       test_must_fail git -C clone rev-parse --symbolic-full-name non-tracking@{u} &&
        (cd clone && git checkout --no-track -b non-tracking) &&
-       test_must_fail full_name non-tracking@{u}
+       test_must_fail git -C clone rev-parse --symbolic-full-name non-tracking@{u}
 '
 
 test_expect_success '<branch>@{u}@{1} resolves correctly' '
        test_commit 6 &&
        (cd clone && git fetch) &&
-       test 5 = $(commit_subject my-side@{u}@{1}) &&
-       test 5 = $(commit_subject my-side@{U}@{1})
+       echo 5 >expect &&
+       commit_subject my-side@{u}@{1} >actual &&
+       test_cmp expect actual &&
+       commit_subject my-side@{U}@{1} >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
@@ -149,7 +159,9 @@ test_expect_success 'checkout other@{u}' '
 '
 
 test_expect_success 'branch@{u} works when tracking a local branch' '
-       test refs/heads/master = "$(full_name local-master@{u})"
+       echo refs/heads/master >expect &&
+       git -C clone rev-parse --symbolic-full-name local-master@{u} >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 'branch@{u} error message when no upstream' '
@@ -203,35 +215,37 @@ test_expect_success 'pull works when tracking a local branch' '
 
 # makes sense if the previous one succeeded
 test_expect_success '@{u} works when tracking a local branch' '
-       test refs/heads/master = "$(full_name @{u})"
+       echo refs/heads/master >expect &&
+       git -C clone rev-parse --symbolic-full-name @{u} >actual &&
+       test_cmp expect actual
 '
 
-commit=$(git rev-parse HEAD)
-cat >expect <<EOF
-commit $commit
-Reflog: master@{0} (C O Mitter <committer@example.com>)
-Reflog message: branch: Created from HEAD
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:15:13 2005 -0700
-
-    3
-EOF
 test_expect_success 'log -g other@{u}' '
+       commit=$(git rev-parse HEAD) &&
+       cat >expect <<-EOF &&
+       commit $commit
+       Reflog: master@{0} (C O Mitter <committer@example.com>)
+       Reflog message: branch: Created from HEAD
+       Author: A U Thor <author@example.com>
+       Date:   Thu Apr 7 15:15:13 2005 -0700
+
+           3
+       EOF
        git log -1 -g other@{u} >actual &&
        test_cmp expect actual
 '
 
-cat >expect <<EOF
-commit $commit
-Reflog: master@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
-Reflog message: branch: Created from HEAD
-Author: A U Thor <author@example.com>
-Date:   Thu Apr 7 15:15:13 2005 -0700
-
-    3
-EOF
-
 test_expect_success 'log -g other@{u}@{now}' '
+       commit=$(git rev-parse HEAD) &&
+       cat >expect <<-EOF &&
+       commit $commit
+       Reflog: master@{Thu Apr 7 15:17:13 2005 -0700} (C O Mitter <committer@example.com>)
+       Reflog message: branch: Created from HEAD
+       Author: A U Thor <author@example.com>
+       Date:   Thu Apr 7 15:15:13 2005 -0700
+
+           3
+       EOF
        git log -1 -g other@{u}@{now} >actual &&
        test_cmp expect actual
 '
index fa0718c730c322bfe5f9e6642c4136a7533be5dc..accfa9aa4bd82a5f13d3ce5e3755f33f47b38e3e 100755 (executable)
@@ -37,7 +37,9 @@ test_expect_success 'setup' '
                git checkout -b foo &&
                test_commit a_foo &&
                git checkout -b bar &&
-               test_commit a_bar
+               test_commit a_bar &&
+               git checkout -b ambiguous_branch_and_file &&
+               test_commit a_ambiguous_branch_and_file
        ) &&
        git init repo_b &&
        (
@@ -46,7 +48,9 @@ test_expect_success 'setup' '
                git checkout -b foo &&
                test_commit b_foo &&
                git checkout -b baz &&
-               test_commit b_baz
+               test_commit b_baz &&
+               git checkout -b ambiguous_branch_and_file &&
+               test_commit b_ambiguous_branch_and_file
        ) &&
        git remote add repo_a repo_a &&
        git remote add repo_b repo_b &&
@@ -75,6 +79,26 @@ test_expect_success 'checkout of branch from multiple remotes fails #1' '
        test_branch master
 '
 
+test_expect_success 'when arg matches multiple remotes, do not fallback to interpreting as pathspec' '
+       # create a file with name matching remote branch name
+       git checkout -b t_ambiguous_branch_and_file &&
+       >ambiguous_branch_and_file &&
+       git add ambiguous_branch_and_file &&
+       git commit -m "ambiguous_branch_and_file" &&
+
+       # modify file to verify that it will not be touched by checkout
+       test_when_finished "git checkout -- ambiguous_branch_and_file" &&
+       echo "file contents" >ambiguous_branch_and_file &&
+       cp ambiguous_branch_and_file expect &&
+
+       test_must_fail git checkout ambiguous_branch_and_file 2>err &&
+
+       test_i18ngrep "matched multiple (2) remote tracking branches" err &&
+
+       # file must not be altered
+       test_cmp expect ambiguous_branch_and_file
+'
+
 test_expect_success 'checkout of branch from multiple remotes fails with advice' '
        git checkout -B master &&
        test_might_fail git branch -D foo &&
index f62fd274404e1e5e5265bdc2b6b367a1d984a7f8..43d31d7948536d2219104c2e6694d447101599a2 100755 (executable)
@@ -109,7 +109,11 @@ test_expect_success 'CRLF delimiters' '
 test_expect_success 'quotes' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" | git checkout --pathspec-from-file=- HEAD^1 &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
+       git checkout --pathspec-from-file=list HEAD^1 &&
 
        cat >expect <<-\EOF &&
        M  fileA.t
@@ -120,7 +124,10 @@ test_expect_success 'quotes' '
 test_expect_success 'quotes not compatible with --pathspec-file-nul' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" >list &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
        test_must_fail git checkout --pathspec-from-file=list --pathspec-file-nul HEAD^1
 '
 
@@ -136,4 +143,21 @@ test_expect_success 'only touches what was listed' '
        verify_expect
 '
 
+test_expect_success 'error conditions' '
+       restore_checkpoint &&
+       echo fileA.t >list &&
+
+       test_must_fail git checkout --pathspec-from-file=list --detach 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --detach" err &&
+
+       test_must_fail git checkout --pathspec-from-file=list --patch 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+
+       test_must_fail git checkout --pathspec-from-file=list -- fileA.t 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+       test_must_fail git checkout --pathspec-file-nul 2>err &&
+       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err
+'
+
 test_done
index 21c3f84459dfe29053bbb0d9a43160a6df33f737..076d0df7fc0602e8d47c03ffa6ddb6dff0b985a9 100755 (executable)
@@ -106,4 +106,21 @@ test_expect_success 'restore --staged adds deleted intent-to-add file back to in
        git diff --cached --exit-code
 '
 
+test_expect_success 'restore --staged invalidates cache tree for deletions' '
+       test_when_finished git reset --hard &&
+       >new1 &&
+       >new2 &&
+       git add new1 new2 &&
+
+       # It is important to commit and then reset here, so that the index
+       # contains a valid cache-tree for the "both" tree.
+       git commit -m both &&
+       git reset --soft HEAD^ &&
+
+       git restore --staged new1 &&
+       git commit -m "just new2" &&
+       git rev-parse HEAD:new2 &&
+       test_must_fail git rev-parse HEAD:new1
+'
+
 test_done
index db58e83735080269a85f80716678ab19838c434b..0d47946e8a9b06f044542e373f3de477e53e3261 100755 (executable)
@@ -109,7 +109,11 @@ test_expect_success 'CRLF delimiters' '
 test_expect_success 'quotes' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" | git restore --pathspec-from-file=- --source=HEAD^1 &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
+       git restore --pathspec-from-file=list --source=HEAD^1 &&
 
        cat >expect <<-\EOF &&
         M fileA.t
@@ -120,7 +124,10 @@ test_expect_success 'quotes' '
 test_expect_success 'quotes not compatible with --pathspec-file-nul' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" >list &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
        test_must_fail git restore --pathspec-from-file=list --pathspec-file-nul --source=HEAD^1
 '
 
@@ -136,4 +143,22 @@ test_expect_success 'only touches what was listed' '
        verify_expect
 '
 
+test_expect_success 'error conditions' '
+       restore_checkpoint &&
+       echo fileA.t >list &&
+       >empty_list &&
+
+       test_must_fail git restore --pathspec-from-file=list --patch --source=HEAD^1 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+
+       test_must_fail git restore --pathspec-from-file=list --source=HEAD^1 -- fileA.t 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+       test_must_fail git restore --pathspec-file-nul --source=HEAD^1 2>err &&
+       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+
+       test_must_fail git restore --pathspec-from-file=empty_list --source=HEAD^1 2>err &&
+       test_i18ngrep -e "you must specify path(s) to restore" err
+'
+
 test_done
diff --git a/t/t2405-worktree-submodule.sh b/t/t2405-worktree-submodule.sh
new file mode 100755 (executable)
index 0000000..e1b2bfd
--- /dev/null
@@ -0,0 +1,90 @@
+#!/bin/sh
+
+test_description='Combination of submodules and multiple worktrees'
+
+. ./test-lib.sh
+
+base_path=$(pwd -P)
+
+test_expect_success 'setup: create origin repos'  '
+       git init origin/sub &&
+       test_commit -C origin/sub file1 &&
+       git init origin/main &&
+       test_commit -C origin/main first &&
+       git -C origin/main submodule add ../sub &&
+       git -C origin/main commit -m "add sub" &&
+       test_commit -C origin/sub "file1 updated" file1 file1updated file1updated &&
+       git -C origin/main/sub pull &&
+       git -C origin/main add sub &&
+       git -C origin/main commit -m "sub updated"
+'
+
+test_expect_success 'setup: clone superproject to create main worktree' '
+       git clone --recursive "$base_path/origin/main" main
+'
+
+rev1_hash_main=$(git --git-dir=origin/main/.git show --pretty=format:%h -q "HEAD~1")
+rev1_hash_sub=$(git --git-dir=origin/sub/.git show --pretty=format:%h -q "HEAD~1")
+
+test_expect_success 'add superproject worktree' '
+       git -C main worktree add "$base_path/worktree" "$rev1_hash_main"
+'
+
+test_expect_failure 'submodule is checked out just after worktree add' '
+       git -C worktree diff --submodule master"^!" >out &&
+       grep "file1 updated" out
+'
+
+test_expect_success 'add superproject worktree and initialize submodules' '
+       git -C main worktree add "$base_path/worktree-submodule-update" "$rev1_hash_main" &&
+       git -C worktree-submodule-update submodule update
+'
+
+test_expect_success 'submodule is checked out just after submodule update in linked worktree' '
+       git -C worktree-submodule-update diff --submodule master"^!" >out &&
+       grep "file1 updated" out
+'
+
+test_expect_success 'add superproject worktree and manually add submodule worktree' '
+       git -C main worktree add "$base_path/linked_submodule" "$rev1_hash_main" &&
+       git -C main/sub worktree add "$base_path/linked_submodule/sub" "$rev1_hash_sub"
+'
+
+test_expect_success 'submodule is checked out after manually adding submodule worktree' '
+       git -C linked_submodule diff --submodule master"^!" >out &&
+       grep "file1 updated" out
+'
+
+test_expect_success 'checkout --recurse-submodules uses $GIT_DIR for submodules in a linked worktree' '
+       git -C main worktree add "$base_path/checkout-recurse" --detach  &&
+       git -C checkout-recurse submodule update --init &&
+       echo "gitdir: ../../main/.git/worktrees/checkout-recurse/modules/sub" >expect-gitfile &&
+       cat checkout-recurse/sub/.git >actual-gitfile &&
+       test_cmp expect-gitfile actual-gitfile &&
+       git -C main/sub rev-parse HEAD >expect-head-main &&
+       git -C checkout-recurse checkout --recurse-submodules HEAD~1 &&
+       cat checkout-recurse/sub/.git >actual-gitfile &&
+       git -C main/sub rev-parse HEAD >actual-head-main &&
+       test_cmp expect-gitfile actual-gitfile &&
+       test_cmp expect-head-main actual-head-main
+'
+
+test_expect_success 'core.worktree is removed in $GIT_DIR/modules/<name>/config, not in $GIT_COMMON_DIR/modules/<name>/config' '
+       echo "../../../sub" >expect-main &&
+       git -C main/sub config --get core.worktree >actual-main &&
+       test_cmp expect-main actual-main &&
+       echo "../../../../../../checkout-recurse/sub" >expect-linked &&
+       git -C checkout-recurse/sub config --get core.worktree >actual-linked &&
+       test_cmp expect-linked actual-linked &&
+       git -C checkout-recurse checkout --recurse-submodules first &&
+       test_expect_code 1 git -C main/.git/worktrees/checkout-recurse/modules/sub config --get core.worktree >linked-config &&
+       test_must_be_empty linked-config &&
+       git -C main/sub config --get core.worktree >actual-main &&
+       test_cmp expect-main actual-main
+'
+
+test_expect_success 'unsetting core.worktree does not prevent running commands directly against the submodule repository' '
+       git -C main/.git/worktrees/checkout-recurse/modules/sub log
+'
+
+test_done
index 0ea858d652fcb5aee58e8a71c6d68cdbcfb974ea..f2c0168941ad043f61a1e082799e75cefa3b5462 100755 (executable)
@@ -425,6 +425,13 @@ test_expect_success 'rm will error out on a modified .gitmodules file unless sta
        git status -s -uno >actual &&
        test_cmp expect actual
 '
+test_expect_success 'rm will not error out on .gitmodules file with zero stat data' '
+       git reset --hard &&
+       git submodule update &&
+       git read-tree HEAD &&
+       git rm submod &&
+       test_path_is_missing submod
+'
 
 test_expect_success 'rm issues a warning when section is not found in .gitmodules' '
        git reset --hard &&
index 12ee321707a33bd8455fb30ea24c57d002c13b68..e36fd0a5d36a23bf2eb3f1db07d8abe8fdfee908 100755 (executable)
@@ -68,6 +68,15 @@ test_expect_success 'revert works (initial)' '
        ! grep . output
 '
 
+test_expect_success 'add untracked (multiple)' '
+       test_when_finished "git reset && rm [1-9]" &&
+       touch $(test_seq 9) &&
+       test_write_lines a "2-5 8-" | git add -i -- [1-9] &&
+       test_write_lines 2 3 4 5 8 9 >expected &&
+       git ls-files [1-9] >output &&
+       test_cmp expected output
+'
+
 test_expect_success 'setup (commit)' '
        echo baseline >file &&
        git add file &&
@@ -561,7 +570,7 @@ test_expect_success 'detect bogus diffFilter output' '
        git reset --hard &&
 
        echo content >test &&
-       test_config interactive.diffFilter "echo too-short" &&
+       test_config interactive.diffFilter "sed 1d" &&
        printf y >y &&
        test_must_fail force_color git add -p <y
 '
index 3cfdb669b7a5a1bc4b915abb0c880f067bfbd7b9..9e35c1fbca68b67336ce2725d91b57a981e8b5c7 100755 (executable)
@@ -97,7 +97,11 @@ test_expect_success 'CRLF delimiters' '
 test_expect_success 'quotes' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" | git add --pathspec-from-file=- &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
+       git add --pathspec-from-file=list &&
 
        cat >expect <<-\EOF &&
        A  fileA.t
@@ -108,7 +112,10 @@ test_expect_success 'quotes' '
 test_expect_success 'quotes not compatible with --pathspec-file-nul' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" >list &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
        test_must_fail git add --pathspec-from-file=list --pathspec-file-nul
 '
 
@@ -124,4 +131,29 @@ test_expect_success 'only touches what was listed' '
        verify_expect
 '
 
+test_expect_success 'error conditions' '
+       restore_checkpoint &&
+       echo fileA.t >list &&
+       >empty_list &&
+
+       test_must_fail git add --pathspec-from-file=list --interactive 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+
+       test_must_fail git add --pathspec-from-file=list --patch 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+
+       test_must_fail git add --pathspec-from-file=list --edit 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --edit" err &&
+
+       test_must_fail git add --pathspec-from-file=list -- fileA.t 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+       test_must_fail git add --pathspec-file-nul 2>err &&
+       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+
+       # This case succeeds, but still prints to stderr
+       git add --pathspec-from-file=empty_list 2>err &&
+       test_i18ngrep -e "Nothing specified, nothing added." err
+'
+
 test_done
index c0f48395432539f807dd9474d71529378f71306b..02255a08bf1718659f9815e37644c07a196ab850 100755 (executable)
@@ -106,7 +106,6 @@ do
                result=success
        fi
        test_expect_$result "hunk header: $i" "
-               test_when_finished 'cat actual' &&      # for debugging only
                git diff -U1 $i >actual &&
                grep '@@ .* @@.*RIGHT' actual
        "
index fcae82fffa7f74a65fc6b475cc5c3eeefda3029a..8c95f152b23b242df5f5aaa0b5b25e6e1826f753 100755 (executable)
@@ -4,8 +4,9 @@ test_description='test diff with a bogus tree containing the null sha1'
 . ./test-lib.sh
 
 test_expect_success 'create bogus tree' '
+       name=$(echo $ZERO_OID | sed -e "s/00/Q/g") &&
        bogus_tree=$(
-               printf "100644 fooQQQQQQQQQQQQQQQQQQQQQ" |
+               printf "100644 fooQ$name" |
                q_to_nul |
                git hash-object -w --stdin -t tree
        )
index 5df6b5e64e0e76e776c337c985dcc1846dc40408..6331f63b127cc7a7ecf63000c99e2876b1ee4dfb 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'set up history with a merge' '
 '
 
 test_expect_success 'log --cc -p --stat --color-moved' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
        commit D
        ---
         D.t | 1 +
@@ -26,7 +26,7 @@ test_expect_success 'log --cc -p --stat --color-moved' '
 
        diff --git a/D.t b/D.t
        new file mode 100644
-       index 0000000..1784810
+       index 0000000..$(git rev-parse --short D:D.t)
        --- /dev/null
        +++ b/D.t
        @@ -0,0 +1 @@
@@ -42,7 +42,7 @@ test_expect_success 'log --cc -p --stat --color-moved' '
 
        diff --git a/C.t b/C.t
        new file mode 100644
-       index 0000000..3cc58df
+       index 0000000..$(git rev-parse --short C:C.t)
        --- /dev/null
        +++ b/C.t
        @@ -0,0 +1 @@
@@ -54,7 +54,7 @@ test_expect_success 'log --cc -p --stat --color-moved' '
 
        diff --git a/B.t b/B.t
        new file mode 100644
-       index 0000000..223b783
+       index 0000000..$(git rev-parse --short B:B.t)
        --- /dev/null
        +++ b/B.t
        @@ -0,0 +1 @@
@@ -66,7 +66,7 @@ test_expect_success 'log --cc -p --stat --color-moved' '
 
        diff --git a/A.t b/A.t
        new file mode 100644
-       index 0000000..f70f10e
+       index 0000000..$(git rev-parse --short A:A.t)
        --- /dev/null
        +++ b/A.t
        @@ -0,0 +1 @@
index 0043930ca6ab31f6cd6a0bf6464ceb281663f9e8..99ed4cc54676de6d56a2caa6a8f38d2c834700f9 100755 (executable)
@@ -8,6 +8,7 @@ test_description='git apply submodule tests'
 . ./test-lib.sh
 
 test_expect_success setup '
+       test_oid_init &&
        cat > create-sm.patch <<EOF &&
 diff --git a/dir/sm b/dir/sm
 new file mode 160000
@@ -15,7 +16,7 @@ index 0000000..0123456
 --- /dev/null
 +++ b/dir/sm
 @@ -0,0 +1 @@
-+Subproject commit 0123456789abcdef0123456789abcdef01234567
++Subproject commit $(test_oid numeric)
 EOF
        cat > remove-sm.patch <<EOF
 diff --git a/dir/sm b/dir/sm
@@ -24,7 +25,7 @@ index 0123456..0000000
 --- a/dir/sm
 +++ /dev/null
 @@ -1 +0,0 @@
--Subproject commit 0123456789abcdef0123456789abcdef01234567
+-Subproject commit $(test_oid numeric)
 EOF
 '
 
index 55b7750ade1c5fd225f830bbbfcb42938abcbe37..831d424c4720924275b9071d4ad5edf8fc73734a 100755 (executable)
@@ -25,6 +25,7 @@ test_description='git rerere
 . ./test-lib.sh
 
 test_expect_success 'setup' '
+       test_oid_init &&
        cat >a1 <<-\EOF &&
        Some title
        ==========
@@ -210,7 +211,7 @@ test_expect_success 'set up for garbage collection tests' '
        echo Hello >$rr/preimage &&
        echo World >$rr/postimage &&
 
-       sha2=4000000000000000000000000000000000000000 &&
+       sha2=$(test_oid deadbeef) &&
        rr2=.git/rr-cache/$sha2 &&
        mkdir $rr2 &&
        echo Hello >$rr2/preimage &&
index 2c9489484a922b03afb8249631e8d30ad53dcbd5..192347a3e1fa50af7e33ef1172c2fbf61120cfa0 100755 (executable)
@@ -87,12 +87,12 @@ test_expect_success 'format %w(,1,2)' '
 '
 
 cat > expect << EOF
-804a787 sixth
-394ef78 fifth
-5d31159 fourth
-2fbe8c0 third
-f7dab8e second
-3a2fdcb initial
+$(git rev-parse --short :/sixth  ) sixth
+$(git rev-parse --short :/fifth  ) fifth
+$(git rev-parse --short :/fourth ) fourth
+$(git rev-parse --short :/third  ) third
+$(git rev-parse --short :/second ) second
+$(git rev-parse --short :/initial) initial
 EOF
 test_expect_success 'oneline' '
 
@@ -173,43 +173,45 @@ test_expect_success 'git config log.follow is overridden by --no-follow' '
        verbose test "$actual" = "$expect"
 '
 
+# Note that these commits are intentionally listed out of order.
+last_three="$(git rev-parse :/fourth :/sixth :/fifth)"
 cat > expect << EOF
-804a787 sixth
-394ef78 fifth
-5d31159 fourth
+$(git rev-parse --short :/sixth ) sixth
+$(git rev-parse --short :/fifth ) fifth
+$(git rev-parse --short :/fourth) fourth
 EOF
 test_expect_success 'git log --no-walk <commits> sorts by commit time' '
-       git log --no-walk --oneline 5d31159 804a787 394ef78 > actual &&
+       git log --no-walk --oneline $last_three > actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'git log --no-walk=sorted <commits> sorts by commit time' '
-       git log --no-walk=sorted --oneline 5d31159 804a787 394ef78 > actual &&
+       git log --no-walk=sorted --oneline $last_three > actual &&
        test_cmp expect actual
 '
 
 cat > expect << EOF
-=== 804a787 sixth
-=== 394ef78 fifth
-=== 5d31159 fourth
+=== $(git rev-parse --short :/sixth ) sixth
+=== $(git rev-parse --short :/fifth ) fifth
+=== $(git rev-parse --short :/fourth) fourth
 EOF
 test_expect_success 'git log --line-prefix="=== " --no-walk <commits> sorts by commit time' '
-       git log --line-prefix="=== " --no-walk --oneline 5d31159 804a787 394ef78 > actual &&
+       git log --line-prefix="=== " --no-walk --oneline $last_three > actual &&
        test_cmp expect actual
 '
 
 cat > expect << EOF
-5d31159 fourth
-804a787 sixth
-394ef78 fifth
+$(git rev-parse --short :/fourth) fourth
+$(git rev-parse --short :/sixth ) sixth
+$(git rev-parse --short :/fifth ) fifth
 EOF
 test_expect_success 'git log --no-walk=unsorted <commits> leaves list of commits as given' '
-       git log --no-walk=unsorted --oneline 5d31159 804a787 394ef78 > actual &&
+       git log --no-walk=unsorted --oneline $last_three > actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'git show <commits> leaves list of commits as given' '
-       git show --oneline -s 5d31159 804a787 394ef78 > actual &&
+       git show --oneline -s $last_three > actual &&
        test_cmp expect actual
 '
 
@@ -957,7 +959,7 @@ cat >expect <<\EOF
 | |
 | | diff --git a/reach.t b/reach.t
 | | new file mode 100644
-| | index 0000000..10c9591
+| | index BEFORE..AFTER
 | | --- /dev/null
 | | +++ b/reach.t
 | | @@ -0,0 +1 @@
@@ -980,7 +982,7 @@ cat >expect <<\EOF
 | | |
 | | |   diff --git a/octopus-b.t b/octopus-b.t
 | | |   new file mode 100644
-| | |   index 0000000..d5fcad0
+| | |   index BEFORE..AFTER
 | | |   --- /dev/null
 | | |   +++ b/octopus-b.t
 | | |   @@ -0,0 +1 @@
@@ -996,7 +998,7 @@ cat >expect <<\EOF
 | |
 | |   diff --git a/octopus-a.t b/octopus-a.t
 | |   new file mode 100644
-| |   index 0000000..11ee015
+| |   index BEFORE..AFTER
 | |   --- /dev/null
 | |   +++ b/octopus-a.t
 | |   @@ -0,0 +1 @@
@@ -1012,7 +1014,7 @@ cat >expect <<\EOF
 |
 |   diff --git a/seventh.t b/seventh.t
 |   new file mode 100644
-|   index 0000000..9744ffc
+|   index BEFORE..AFTER
 |   --- /dev/null
 |   +++ b/seventh.t
 |   @@ -0,0 +1 @@
@@ -1046,7 +1048,7 @@ cat >expect <<\EOF
 | | | |
 | | | | diff --git a/tangle-a b/tangle-a
 | | | | new file mode 100644
-| | | | index 0000000..7898192
+| | | | index BEFORE..AFTER
 | | | | --- /dev/null
 | | | | +++ b/tangle-a
 | | | | @@ -0,0 +1 @@
@@ -1068,7 +1070,7 @@ cat >expect <<\EOF
 | | | |
 | | | |   diff --git a/2 b/2
 | | | |   new file mode 100644
-| | | |   index 0000000..0cfbf08
+| | | |   index BEFORE..AFTER
 | | | |   --- /dev/null
 | | | |   +++ b/2
 | | | |   @@ -0,0 +1 @@
@@ -1084,7 +1086,7 @@ cat >expect <<\EOF
 | | | |
 | | | | diff --git a/1 b/1
 | | | | new file mode 100644
-| | | | index 0000000..d00491f
+| | | | index BEFORE..AFTER
 | | | | --- /dev/null
 | | | | +++ b/1
 | | | | @@ -0,0 +1 @@
@@ -1100,7 +1102,7 @@ cat >expect <<\EOF
 | | | |
 | | | | diff --git a/one b/one
 | | | | new file mode 100644
-| | | | index 0000000..9a33383
+| | | | index BEFORE..AFTER
 | | | | --- /dev/null
 | | | | +++ b/one
 | | | | @@ -0,0 +1 @@
@@ -1116,7 +1118,7 @@ cat >expect <<\EOF
 | | |
 | | |   diff --git a/a/two b/a/two
 | | |   deleted file mode 100644
-| | |   index 9245af5..0000000
+| | |   index BEFORE..AFTER
 | | |   --- a/a/two
 | | |   +++ /dev/null
 | | |   @@ -1 +0,0 @@
@@ -1132,7 +1134,7 @@ cat >expect <<\EOF
 | | |
 | | | diff --git a/a/two b/a/two
 | | | new file mode 100644
-| | | index 0000000..9245af5
+| | | index BEFORE..AFTER
 | | | --- /dev/null
 | | | +++ b/a/two
 | | | @@ -0,0 +1 @@
@@ -1148,7 +1150,7 @@ cat >expect <<\EOF
 | |
 | |   diff --git a/ein b/ein
 | |   new file mode 100644
-| |   index 0000000..9d7e69f
+| |   index BEFORE..AFTER
 | |   --- /dev/null
 | |   +++ b/ein
 | |   @@ -0,0 +1 @@
@@ -1165,14 +1167,14 @@ cat >expect <<\EOF
 |
 |   diff --git a/ichi b/ichi
 |   new file mode 100644
-|   index 0000000..9d7e69f
+|   index BEFORE..AFTER
 |   --- /dev/null
 |   +++ b/ichi
 |   @@ -0,0 +1 @@
 |   +ichi
 |   diff --git a/one b/one
 |   deleted file mode 100644
-|   index 9d7e69f..0000000
+|   index BEFORE..AFTER
 |   --- a/one
 |   +++ /dev/null
 |   @@ -1 +0,0 @@
@@ -1187,7 +1189,7 @@ cat >expect <<\EOF
 |  1 file changed, 1 insertion(+), 1 deletion(-)
 |
 | diff --git a/one b/one
-| index 5626abf..9d7e69f 100644
+| index BEFORE..AFTER 100644
 | --- a/one
 | +++ b/one
 | @@ -1 +1 @@
@@ -1204,7 +1206,7 @@ cat >expect <<\EOF
 
   diff --git a/one b/one
   new file mode 100644
-  index 0000000..5626abf
+  index BEFORE..AFTER
   --- /dev/null
   +++ b/one
   @@ -0,0 +1 @@
@@ -1221,7 +1223,8 @@ sanitize_output () {
            -e 's/, 0 insertions(+)//' \
            -e 's/ 1 files changed, / 1 file changed, /' \
            -e 's/, 1 deletions(-)/, 1 deletion(-)/' \
-           -e 's/, 1 insertions(+)/, 1 insertion(+)/'
+           -e 's/, 1 insertions(+)/, 1 insertion(+)/' \
+           -e 's/index [0-9a-f]*\.\.[0-9a-f]*/index BEFORE..AFTER/'
 }
 
 test_expect_success 'log --graph with diff and stats' '
@@ -1247,7 +1250,7 @@ cat >expect <<\EOF
 *** | |
 *** | | diff --git a/reach.t b/reach.t
 *** | | new file mode 100644
-*** | | index 0000000..10c9591
+*** | | index BEFORE..AFTER
 *** | | --- /dev/null
 *** | | +++ b/reach.t
 *** | | @@ -0,0 +1 @@
@@ -1270,7 +1273,7 @@ cat >expect <<\EOF
 *** | | |
 *** | | |   diff --git a/octopus-b.t b/octopus-b.t
 *** | | |   new file mode 100644
-*** | | |   index 0000000..d5fcad0
+*** | | |   index BEFORE..AFTER
 *** | | |   --- /dev/null
 *** | | |   +++ b/octopus-b.t
 *** | | |   @@ -0,0 +1 @@
@@ -1286,7 +1289,7 @@ cat >expect <<\EOF
 *** | |
 *** | |   diff --git a/octopus-a.t b/octopus-a.t
 *** | |   new file mode 100644
-*** | |   index 0000000..11ee015
+*** | |   index BEFORE..AFTER
 *** | |   --- /dev/null
 *** | |   +++ b/octopus-a.t
 *** | |   @@ -0,0 +1 @@
@@ -1302,7 +1305,7 @@ cat >expect <<\EOF
 *** |
 *** |   diff --git a/seventh.t b/seventh.t
 *** |   new file mode 100644
-*** |   index 0000000..9744ffc
+*** |   index BEFORE..AFTER
 *** |   --- /dev/null
 *** |   +++ b/seventh.t
 *** |   @@ -0,0 +1 @@
@@ -1336,7 +1339,7 @@ cat >expect <<\EOF
 *** | | | |
 *** | | | | diff --git a/tangle-a b/tangle-a
 *** | | | | new file mode 100644
-*** | | | | index 0000000..7898192
+*** | | | | index BEFORE..AFTER
 *** | | | | --- /dev/null
 *** | | | | +++ b/tangle-a
 *** | | | | @@ -0,0 +1 @@
@@ -1358,7 +1361,7 @@ cat >expect <<\EOF
 *** | | | |
 *** | | | |   diff --git a/2 b/2
 *** | | | |   new file mode 100644
-*** | | | |   index 0000000..0cfbf08
+*** | | | |   index BEFORE..AFTER
 *** | | | |   --- /dev/null
 *** | | | |   +++ b/2
 *** | | | |   @@ -0,0 +1 @@
@@ -1374,7 +1377,7 @@ cat >expect <<\EOF
 *** | | | |
 *** | | | | diff --git a/1 b/1
 *** | | | | new file mode 100644
-*** | | | | index 0000000..d00491f
+*** | | | | index BEFORE..AFTER
 *** | | | | --- /dev/null
 *** | | | | +++ b/1
 *** | | | | @@ -0,0 +1 @@
@@ -1390,7 +1393,7 @@ cat >expect <<\EOF
 *** | | | |
 *** | | | | diff --git a/one b/one
 *** | | | | new file mode 100644
-*** | | | | index 0000000..9a33383
+*** | | | | index BEFORE..AFTER
 *** | | | | --- /dev/null
 *** | | | | +++ b/one
 *** | | | | @@ -0,0 +1 @@
@@ -1406,7 +1409,7 @@ cat >expect <<\EOF
 *** | | |
 *** | | |   diff --git a/a/two b/a/two
 *** | | |   deleted file mode 100644
-*** | | |   index 9245af5..0000000
+*** | | |   index BEFORE..AFTER
 *** | | |   --- a/a/two
 *** | | |   +++ /dev/null
 *** | | |   @@ -1 +0,0 @@
@@ -1422,7 +1425,7 @@ cat >expect <<\EOF
 *** | | |
 *** | | | diff --git a/a/two b/a/two
 *** | | | new file mode 100644
-*** | | | index 0000000..9245af5
+*** | | | index BEFORE..AFTER
 *** | | | --- /dev/null
 *** | | | +++ b/a/two
 *** | | | @@ -0,0 +1 @@
@@ -1438,7 +1441,7 @@ cat >expect <<\EOF
 *** | |
 *** | |   diff --git a/ein b/ein
 *** | |   new file mode 100644
-*** | |   index 0000000..9d7e69f
+*** | |   index BEFORE..AFTER
 *** | |   --- /dev/null
 *** | |   +++ b/ein
 *** | |   @@ -0,0 +1 @@
@@ -1455,14 +1458,14 @@ cat >expect <<\EOF
 *** |
 *** |   diff --git a/ichi b/ichi
 *** |   new file mode 100644
-*** |   index 0000000..9d7e69f
+*** |   index BEFORE..AFTER
 *** |   --- /dev/null
 *** |   +++ b/ichi
 *** |   @@ -0,0 +1 @@
 *** |   +ichi
 *** |   diff --git a/one b/one
 *** |   deleted file mode 100644
-*** |   index 9d7e69f..0000000
+*** |   index BEFORE..AFTER
 *** |   --- a/one
 *** |   +++ /dev/null
 *** |   @@ -1 +0,0 @@
@@ -1477,7 +1480,7 @@ cat >expect <<\EOF
 *** |  1 file changed, 1 insertion(+), 1 deletion(-)
 *** |
 *** | diff --git a/one b/one
-*** | index 5626abf..9d7e69f 100644
+*** | index BEFORE..AFTER 100644
 *** | --- a/one
 *** | +++ b/one
 *** | @@ -1 +1 @@
@@ -1494,7 +1497,7 @@ cat >expect <<\EOF
 ***
 ***   diff --git a/one b/one
 ***   new file mode 100644
-***   index 0000000..5626abf
+***   index BEFORE..AFTER
 ***   --- /dev/null
 ***   +++ b/one
 ***   @@ -0,0 +1 @@
@@ -1709,10 +1712,10 @@ test_expect_success 'set up --source tests' '
 '
 
 test_expect_success 'log --source paints branch names' '
-       cat >expect <<-\EOF &&
-       09e12a9 source-b three
-       8e393e1 source-a two
-       1ac6c77 source-b one
+       cat >expect <<-EOF &&
+       $(git rev-parse --short :/three)        source-b three
+       $(git rev-parse --short :/two  )        source-a two
+       $(git rev-parse --short :/one  )        source-b one
        EOF
        git log --oneline --source source-a source-b >actual &&
        test_cmp expect actual
@@ -1720,19 +1723,19 @@ test_expect_success 'log --source paints branch names' '
 
 test_expect_success 'log --source paints tag names' '
        git tag -m tagged source-tag &&
-       cat >expect <<-\EOF &&
-       09e12a9 source-tag three
-       8e393e1 source-a two
-       1ac6c77 source-tag one
+       cat >expect <<-EOF &&
+       $(git rev-parse --short :/three)        source-tag three
+       $(git rev-parse --short :/two  )        source-a two
+       $(git rev-parse --short :/one  )        source-tag one
        EOF
        git log --oneline --source source-tag source-a >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'log --source paints symmetric ranges' '
-       cat >expect <<-\EOF &&
-       09e12a9 source-b three
-       8e393e1 source-a two
+       cat >expect <<-EOF &&
+       $(git rev-parse --short :/three)        source-b three
+       $(git rev-parse --short :/two  )        source-a two
        EOF
        git log --oneline --source source-a...source-b >actual &&
        test_cmp expect actual
index 0288c17ec60b803d2815fb1b704c35f74e4a7753..8ff8bd84c75e0fd4fd4c75e911b6abfca18a70fc 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success 'setup' '
 
 test_expect_success 'patch-id output is well-formed' '
        git log -p -1 | git patch-id >output &&
-       grep "^[a-f0-9]\{40\} $(git rev-parse HEAD)$" output
+       grep "^$OID_REGEX $(git rev-parse HEAD)$" output
 '
 
 #calculate patch id. Make sure output is not empty.
index 5661ed5881f4831a36637cfd2dd96f1a9c19e504..1d0d3240ff69c9e8b5261939a8fa903de65cabd5 100755 (executable)
@@ -311,4 +311,66 @@ test_expect_success 'log --graph with multiple tips and colors' '
        test_cmp expect.colors actual.colors
 '
 
+test_expect_success 'log --graph with multiple tips' '
+       git checkout --orphan 7_1 &&
+       test_commit 7_A &&
+       test_commit 7_B &&
+       test_commit 7_C &&
+       git checkout -b 7_2 7_1~2 &&
+       test_commit 7_D &&
+       test_commit 7_E &&
+       git checkout -b 7_3 7_1~1 &&
+       test_commit 7_F &&
+       test_commit 7_G &&
+       git checkout -b 7_4 7_2~1 &&
+       test_commit 7_H &&
+       git checkout -b 7_5 7_1~2 &&
+       test_commit 7_I &&
+       git checkout -b 7_6 7_3~1 &&
+       test_commit 7_J &&
+       git checkout -b M_1 7_1 &&
+       git merge --no-ff 7_2 -m 7_M1 &&
+       git checkout -b M_3 7_3 &&
+       git merge --no-ff 7_4 -m 7_M2 &&
+       git checkout -b M_5 7_5 &&
+       git merge --no-ff 7_6 -m 7_M3 &&
+       git checkout -b M_7 7_1 &&
+       git merge --no-ff 7_2 7_3 -m 7_M4 &&
+
+       check_graph M_1 M_3 M_5 M_7 <<-\EOF
+       *   7_M1
+       |\
+       | | *   7_M2
+       | | |\
+       | | | * 7_H
+       | | | | *   7_M3
+       | | | | |\
+       | | | | | * 7_J
+       | | | | * | 7_I
+       | | | | | | *   7_M4
+       | |_|_|_|_|/|\
+       |/| | | | |/ /
+       | | |_|_|/| /
+       | |/| | | |/
+       | | | |_|/|
+       | | |/| | |
+       | | * | | | 7_G
+       | | | |_|/
+       | | |/| |
+       | | * | | 7_F
+       | * | | | 7_E
+       | | |/ /
+       | |/| |
+       | * | | 7_D
+       | | |/
+       | |/|
+       * | | 7_C
+       | |/
+       |/|
+       * | 7_B
+       |/
+       * 7_A
+       EOF
+'
+
 test_done
index d87cc7d9efde9d728825ae84b4a329b720b6d821..e59601e5fe9b75eda9f4daaa801c1ed6c40d6fc6 100755 (executable)
@@ -11,16 +11,16 @@ test_expect_success setup '
 '
 
 test_expect_success 'file add A, !B' '
-       cat >expected <<\EXPECTED &&
+       git reset --hard initial &&
+       test_commit "add-a-not-b" "ONE" "AAA" &&
+       git merge-tree initial initial add-a-not-b >actual &&
+       cat >expected <<EXPECTED &&
 added in remote
-  their  100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
+  their  100644 $(git rev-parse HEAD:ONE) ONE
 @@ -0,0 +1 @@
 +AAA
 EXPECTED
 
-       git reset --hard initial &&
-       test_commit "add-a-not-b" "ONE" "AAA" &&
-       git merge-tree initial initial add-a-not-b >actual &&
        test_cmp expected actual
 '
 
@@ -41,10 +41,15 @@ test_expect_success 'file add A, B (same)' '
 '
 
 test_expect_success 'file add A, B (different)' '
-       cat >expected <<\EXPECTED &&
+       git reset --hard initial &&
+       test_commit "add-a-b-diff-A" "ONE" "AAA" &&
+       git reset --hard initial &&
+       test_commit "add-a-b-diff-B" "ONE" "BBB" &&
+       git merge-tree initial add-a-b-diff-A add-a-b-diff-B >actual &&
+       cat >expected <<EXPECTED &&
 added in both
-  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
-  their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
+  our    100644 $(git rev-parse add-a-b-diff-A:ONE) ONE
+  their  100644 $(git rev-parse add-a-b-diff-B:ONE) ONE
 @@ -1 +1,5 @@
 +<<<<<<< .our
  AAA
@@ -53,11 +58,6 @@ added in both
 +>>>>>>> .their
 EXPECTED
 
-       git reset --hard initial &&
-       test_commit "add-a-b-diff-A" "ONE" "AAA" &&
-       git reset --hard initial &&
-       test_commit "add-a-b-diff-B" "ONE" "BBB" &&
-       git merge-tree initial add-a-b-diff-A add-a-b-diff-B >actual &&
        test_cmp expected actual
 '
 
@@ -69,18 +69,18 @@ test_expect_success 'file change A, !B' '
 '
 
 test_expect_success 'file change !A, B' '
-       cat >expected <<\EXPECTED &&
+       git reset --hard initial &&
+       test_commit "change-not-a-b" "initial-file" "BBB" &&
+       git merge-tree initial initial change-not-a-b >actual &&
+       cat >expected <<EXPECTED &&
 merged
-  result 100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
-  our    100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+  result 100644 $(git rev-parse change-a-not-b:initial-file) initial-file
+  our    100644 $(git rev-parse initial:initial-file       ) initial-file
 @@ -1 +1 @@
 -initial
 +BBB
 EXPECTED
 
-       git reset --hard initial &&
-       test_commit "change-not-a-b" "initial-file" "BBB" &&
-       git merge-tree initial initial change-not-a-b >actual &&
        test_cmp expected actual
 '
 
@@ -94,11 +94,16 @@ test_expect_success 'file change A, B (same)' '
 '
 
 test_expect_success 'file change A, B (different)' '
-       cat >expected <<\EXPECTED &&
+       git reset --hard initial &&
+       test_commit "change-a-b-diff-A" "initial-file" "AAA" &&
+       git reset --hard initial &&
+       test_commit "change-a-b-diff-B" "initial-file" "BBB" &&
+       git merge-tree initial change-a-b-diff-A change-a-b-diff-B >actual &&
+       cat >expected <<EXPECTED &&
 changed in both
-  base   100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
-  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d initial-file
-  their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 initial-file
+  base   100644 $(git rev-parse initial:initial-file          ) initial-file
+  our    100644 $(git rev-parse change-a-b-diff-A:initial-file) initial-file
+  their  100644 $(git rev-parse change-a-b-diff-B:initial-file) initial-file
 @@ -1 +1,5 @@
 +<<<<<<< .our
  AAA
@@ -107,34 +112,10 @@ changed in both
 +>>>>>>> .their
 EXPECTED
 
-       git reset --hard initial &&
-       test_commit "change-a-b-diff-A" "initial-file" "AAA" &&
-       git reset --hard initial &&
-       test_commit "change-a-b-diff-B" "initial-file" "BBB" &&
-       git merge-tree initial change-a-b-diff-A change-a-b-diff-B >actual &&
        test_cmp expected actual
 '
 
 test_expect_success 'file change A, B (mixed)' '
-       cat >expected <<\EXPECTED &&
-changed in both
-  base   100644 f4f1f998c7776568c4ff38f516d77fef9399b5a7 ONE
-  our    100644 af14c2c3475337c73759d561ef70b59e5c731176 ONE
-  their  100644 372d761493f524d44d59bd24700c3bdf914c973c ONE
-@@ -7,7 +7,11 @@
- AAA
- AAA
- AAA
-+<<<<<<< .our
- BBB
-+=======
-+CCC
-+>>>>>>> .their
- AAA
- AAA
- AAA
-EXPECTED
-
        git reset --hard initial &&
        test_commit "change-a-b-mix-base" "ONE" "
 AAA
@@ -159,6 +140,26 @@ AAA" &&
                "$(sed -e "1{s/AAA/BBB/;}" -e "10{s/AAA/CCC/;}" <ONE)" &&
        git merge-tree change-a-b-mix-base change-a-b-mix-A change-a-b-mix-B \
                >actual &&
+
+       cat >expected <<EXPECTED &&
+changed in both
+  base   100644 $(git rev-parse change-a-b-mix-base:ONE) ONE
+  our    100644 $(git rev-parse change-a-b-mix-A:ONE   ) ONE
+  their  100644 $(git rev-parse change-a-b-mix-B:ONE   ) ONE
+@@ -7,7 +7,11 @@
+ AAA
+ AAA
+ AAA
++<<<<<<< .our
+ BBB
++=======
++CCC
++>>>>>>> .their
+ AAA
+ AAA
+ AAA
+EXPECTED
+
        test_cmp expected actual
 '
 
@@ -173,20 +174,20 @@ test_expect_success 'file remove A, !B' '
 '
 
 test_expect_success 'file remove !A, B' '
-       cat >expected <<\EXPECTED &&
-removed in remote
-  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
-  our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
-@@ -1 +0,0 @@
--AAA
-EXPECTED
-
        git reset --hard initial &&
        test_commit "rm-not-a-b-base" "ONE" "AAA" &&
        git rm ONE &&
        git commit -m "rm-not-a-b" &&
        git tag "rm-not-a-b" &&
        git merge-tree rm-a-not-b-base rm-a-not-b-base rm-a-not-b >actual &&
+       cat >expected <<EXPECTED &&
+removed in remote
+  base   100644 $(git rev-parse rm-a-not-b-base:ONE) ONE
+  our    100644 $(git rev-parse rm-a-not-b-base:ONE) ONE
+@@ -1 +0,0 @@
+-AAA
+EXPECTED
+
        test_cmp expected actual
 '
 
@@ -201,14 +202,6 @@ test_expect_success 'file remove A, B (same)' '
 '
 
 test_expect_success 'file change A, remove B' '
-       cat >expected <<\EXPECTED &&
-removed in remote
-  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
-  our    100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
-@@ -1 +0,0 @@
--BBB
-EXPECTED
-
        git reset --hard initial &&
        test_commit "change-a-rm-b-base" "ONE" "AAA" &&
        test_commit "change-a-rm-b-A" "ONE" "BBB" &&
@@ -218,16 +211,18 @@ EXPECTED
        git tag "change-a-rm-b-B" &&
        git merge-tree change-a-rm-b-base change-a-rm-b-A change-a-rm-b-B \
                >actual &&
+       cat >expected <<EXPECTED &&
+removed in remote
+  base   100644 $(git rev-parse change-a-rm-b-base:ONE) ONE
+  our    100644 $(git rev-parse change-a-rm-b-A:ONE   ) ONE
+@@ -1 +0,0 @@
+-BBB
+EXPECTED
+
        test_cmp expected actual
 '
 
 test_expect_success 'file remove A, change B' '
-       cat >expected <<\EXPECTED &&
-removed in local
-  base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d ONE
-  their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 ONE
-EXPECTED
-
        git reset --hard initial &&
        test_commit "rm-a-change-b-base" "ONE" "AAA" &&
 
@@ -238,6 +233,11 @@ EXPECTED
        test_commit "rm-a-change-b-B" "ONE" "BBB" &&
        git merge-tree rm-a-change-b-base rm-a-change-b-A rm-a-change-b-B \
                >actual &&
+       cat >expected <<EXPECTED &&
+removed in local
+  base   100644 $(git rev-parse rm-a-change-b-base:ONE) ONE
+  their  100644 $(git rev-parse rm-a-change-b-B:ONE   ) ONE
+EXPECTED
        test_cmp expected actual
 '
 
@@ -250,10 +250,17 @@ test_expect_success 'tree add A, B (same)' '
 '
 
 test_expect_success 'tree add A, B (different)' '
-       cat >expect <<-\EOF &&
+       git reset --hard initial &&
+       mkdir sub &&
+       test_commit "add sub/file" "sub/file" "AAA" add-tree-a-b-A &&
+       git reset --hard initial &&
+       mkdir sub &&
+       test_commit "add sub/file" "sub/file" "BBB" add-tree-a-b-B &&
+       git merge-tree initial add-tree-a-b-A add-tree-a-b-B >actual &&
+       cat >expect <<-EOF &&
        added in both
-         our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
-         their  100644 ba629238ca89489f2b350e196ca445e09d8bb834 sub/file
+         our    100644 $(git rev-parse add-tree-a-b-A:sub/file) sub/file
+         their  100644 $(git rev-parse add-tree-a-b-B:sub/file) sub/file
        @@ -1 +1,5 @@
        +<<<<<<< .our
         AAA
@@ -261,24 +268,10 @@ test_expect_success 'tree add A, B (different)' '
        +BBB
        +>>>>>>> .their
        EOF
-       git reset --hard initial &&
-       mkdir sub &&
-       test_commit "add sub/file" "sub/file" "AAA" add-tree-a-b-A &&
-       git reset --hard initial &&
-       mkdir sub &&
-       test_commit "add sub/file" "sub/file" "BBB" add-tree-a-b-B &&
-       git merge-tree initial add-tree-a-b-A add-tree-a-b-B >actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'tree unchanged A, removed B' '
-       cat >expect <<-\EOF &&
-       removed in remote
-         base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
-         our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d sub/file
-       @@ -1 +0,0 @@
-       -AAA
-       EOF
        git reset --hard initial &&
        mkdir sub &&
        test_commit "add sub/file" "sub/file" "AAA" tree-remove-b-initial &&
@@ -287,6 +280,13 @@ test_expect_success 'tree unchanged A, removed B' '
        git commit -m "remove sub/file" &&
        git tag tree-remove-b-B &&
        git merge-tree tree-remove-b-initial tree-remove-b-initial tree-remove-b-B >actual &&
+       cat >expect <<-EOF &&
+       removed in remote
+         base   100644 $(git rev-parse tree-remove-b-initial:sub/file) sub/file
+         our    100644 $(git rev-parse tree-remove-b-initial:sub/file) sub/file
+       @@ -1 +0,0 @@
+       -AAA
+       EOF
        test_cmp expect actual
 '
 
@@ -296,14 +296,14 @@ test_expect_success 'turn file to tree' '
        mkdir initial-file &&
        test_commit "turn-file-to-tree" "initial-file/ONE" "CCC" &&
        git merge-tree initial initial turn-file-to-tree >actual &&
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
        added in remote
-         their  100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 initial-file/ONE
+         their  100644 $(git rev-parse turn-file-to-tree:initial-file/ONE) initial-file/ONE
        @@ -0,0 +1 @@
        +CCC
        removed in remote
-         base   100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
-         our    100644 e79c5e8f964493290a409888d5413a737e8e5dd5 initial-file
+         base   100644 $(git rev-parse initial:initial-file) initial-file
+         our    100644 $(git rev-parse initial:initial-file) initial-file
        @@ -1 +0,0 @@
        -initial
        EOF
@@ -318,14 +318,14 @@ test_expect_success 'turn tree to file' '
        rm -fr dir &&
        test_commit "make-file" "dir" "CCC" &&
        git merge-tree add-tree add-another-tree make-file >actual &&
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
        removed in remote
-         base   100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
-         our    100644 43d5a8ed6ef6c00ff775008633f95787d088285d dir/path
+         base   100644 $(git rev-parse add-tree:dir/path) dir/path
+         our    100644 $(git rev-parse add-tree:dir/path) dir/path
        @@ -1 +0,0 @@
        -AAA
        added in remote
-         their  100644 43aa4fdec31eb92e1fdc2f0ce6ea9ddb7c32bcf7 dir
+         their  100644 $(git rev-parse make-file:dir) dir
        @@ -0,0 +1 @@
        +CCC
        EOF
index 3f03de60185738a07f42e291eae649598930d81c..81cf118cb6622b1984811b62df9dc7b9c957e1be 100755 (executable)
@@ -481,7 +481,7 @@ test_expect_success 'detect bad version' '
 '
 
 test_expect_success 'detect bad hash version' '
-       corrupt_graph_and_verify $GRAPH_BYTE_HASH "\02" \
+       corrupt_graph_and_verify $GRAPH_BYTE_HASH "\03" \
                "hash version"
 '
 
index cd2f87be6afe241a35e7c3b20fac09d10908f633..43a7a66c9d1b50640775d6b38df7a31b32e8e548 100755 (executable)
@@ -28,6 +28,20 @@ midx_read_expect () {
        test_cmp expect actual
 }
 
+test_expect_success 'setup' '
+       test_oid_init &&
+       test_oid_cache <<-EOF
+       idxoff sha1:2999
+       idxoff sha256:3739
+
+       packnameoff sha1:652
+       packnameoff sha256:940
+
+       fanoutoff sha1:1
+       fanoutoff sha256:3
+       EOF
+'
+
 test_expect_success 'write midx with no packs' '
        test_when_finished rm -f pack/multi-pack-index &&
        git multi-pack-index --object-dir=. write &&
@@ -225,7 +239,7 @@ test_expect_success 'verify bad signature' '
                "multi-pack-index signature"
 '
 
-HASH_LEN=20
+HASH_LEN=$(test_oid rawsz)
 NUM_OBJECTS=74
 MIDX_BYTE_VERSION=4
 MIDX_BYTE_OID_VERSION=5
@@ -238,9 +252,9 @@ MIDX_CHUNK_LOOKUP_WIDTH=12
 MIDX_OFFSET_PACKNAMES=$(($MIDX_HEADER_SIZE + \
                         $MIDX_NUM_CHUNKS * $MIDX_CHUNK_LOOKUP_WIDTH))
 MIDX_BYTE_PACKNAME_ORDER=$(($MIDX_OFFSET_PACKNAMES + 2))
-MIDX_OFFSET_OID_FANOUT=$(($MIDX_OFFSET_PACKNAMES + 652))
+MIDX_OFFSET_OID_FANOUT=$(($MIDX_OFFSET_PACKNAMES + $(test_oid packnameoff)))
 MIDX_OID_FANOUT_WIDTH=4
-MIDX_BYTE_OID_FANOUT_ORDER=$((MIDX_OFFSET_OID_FANOUT + 250 * $MIDX_OID_FANOUT_WIDTH + 1))
+MIDX_BYTE_OID_FANOUT_ORDER=$((MIDX_OFFSET_OID_FANOUT + 250 * $MIDX_OID_FANOUT_WIDTH + $(test_oid fanoutoff)))
 MIDX_OFFSET_OID_LOOKUP=$(($MIDX_OFFSET_OID_FANOUT + 256 * $MIDX_OID_FANOUT_WIDTH))
 MIDX_BYTE_OID_LOOKUP=$(($MIDX_OFFSET_OID_LOOKUP + 16 * $HASH_LEN))
 MIDX_OFFSET_OBJECT_OFFSETS=$(($MIDX_OFFSET_OID_LOOKUP + $NUM_OBJECTS * $HASH_LEN))
@@ -304,12 +318,12 @@ test_expect_success 'verify incorrect pack-int-id' '
 '
 
 test_expect_success 'verify incorrect offset' '
-       corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\07" $objdir \
+       corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
                "incorrect object offset"
 '
 
 test_expect_success 'git-fsck incorrect offset' '
-       corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\07" $objdir \
+       corrupt_midx_and_verify $MIDX_BYTE_OFFSET "\377" $objdir \
                "incorrect object offset" \
                "git -c core.multipackindex=true fsck"
 '
@@ -387,7 +401,7 @@ test_expect_success 'force some 64-bit offsets with pack-objects' '
        pack64=$(git pack-objects --index-version=2,0x40 objects64/pack/test-64 <obj-list) &&
        idx64=objects64/pack/test-64-$pack64.idx &&
        chmod u+w $idx64 &&
-       corrupt_data $idx64 2999 "\02" &&
+       corrupt_data $idx64 $(test_oid idxoff) "\02" &&
        midx64=$(git multi-pack-index --object-dir=objects64 write) &&
        midx_read_expect 1 63 5 objects64 " large-offsets"
 '
index c24823431f2314169874adc705188f40da4c1618..53b2e6b4555de7d7cc27c727e2e7a3d7a4ff3f5b 100755 (executable)
@@ -11,7 +11,14 @@ test_expect_success 'setup repo' '
        git config gc.writeCommitGraph false &&
        infodir=".git/objects/info" &&
        graphdir="$infodir/commit-graphs" &&
-       test_oid_init
+       test_oid_init &&
+       test_oid_cache <<-EOM
+       shallow sha1:1760
+       shallow sha256:2064
+
+       base sha1:1376
+       base sha256:1496
+       EOM
 '
 
 graph_read_expect() {
@@ -248,7 +255,7 @@ test_expect_success 'verify hashes along chain, even in shallow' '
                cd verify &&
                git commit-graph verify &&
                base_file=$graphdir/graph-$(head -n 1 $graphdir/commit-graph-chain).graph &&
-               corrupt_file "$base_file" 1760 "\01" &&
+               corrupt_file "$base_file" $(test_oid shallow) "\01" &&
                test_must_fail git commit-graph verify --shallow 2>test_err &&
                grep -v "^+" test_err >err &&
                test_i18ngrep "incorrect checksum" err
@@ -275,7 +282,7 @@ test_expect_success 'warn on base graph chunk incorrect' '
                cd base-chunk &&
                git commit-graph verify &&
                base_file=$graphdir/graph-$(tail -n 1 $graphdir/commit-graph-chain).graph &&
-               corrupt_file "$base_file" 1376 "\01" &&
+               corrupt_file "$base_file" $(test_oid base) "\01" &&
                git commit-graph verify --shallow 2>test_err &&
                grep -v "^+" test_err >err &&
                test_i18ngrep "commit-graph chain does not match" err
index fdfe179b11885be7fdc49ed3732d0dfe5d3537bc..645b4c78d356971cdf096456d7e90463f58cd0e3 100755 (executable)
@@ -4,6 +4,7 @@ test_description='fetch/receive strict mode'
 . ./test-lib.sh
 
 test_expect_success 'setup and inject "corrupt or missing" object' '
+       test_oid_init &&
        echo hello >greetings &&
        git add greetings &&
        git commit -m greetings &&
@@ -144,11 +145,11 @@ test_expect_success 'fsck with no skipList input' '
 
 test_expect_success 'setup sorted and unsorted skipLists' '
        cat >SKIP.unsorted <<-EOF &&
-       0000000000000000000000000000000000000004
-       0000000000000000000000000000000000000002
+       $(test_oid 004)
+       $(test_oid 002)
        $commit
-       0000000000000000000000000000000000000001
-       0000000000000000000000000000000000000003
+       $(test_oid 001)
+       $(test_oid 003)
        EOF
        sort SKIP.unsorted >SKIP.sorted
 '
@@ -172,14 +173,14 @@ test_expect_success 'fsck with invalid or bogus skipList input' '
 test_expect_success 'fsck with other accepted skipList input (comments & empty lines)' '
        cat >SKIP.with-comment <<-EOF &&
        # Some bad commit
-       0000000000000000000000000000000000000001
+       $(test_oid 001)
        EOF
        test_must_fail git -c fsck.skipList=SKIP.with-comment fsck 2>err-with-comment &&
        test_i18ngrep "missingEmail" err-with-comment &&
        cat >SKIP.with-empty-line <<-EOF &&
-       0000000000000000000000000000000000000001
+       $(test_oid 001)
 
-       0000000000000000000000000000000000000002
+       $(test_oid 002)
        EOF
        test_must_fail git -c fsck.skipList=SKIP.with-empty-line fsck 2>err-with-empty-line &&
        test_i18ngrep "missingEmail" err-with-empty-line
@@ -204,7 +205,7 @@ test_expect_success 'fsck with exhaustive accepted skipList input (various types
        echo " # Comment after whitespace" >>SKIP.exhaustive &&
        echo "$commit # Our bad commit (with leading whitespace and trailing comment)" >>SKIP.exhaustive &&
        echo "# Some bad commit (leading whitespace)" >>SKIP.exhaustive &&
-       echo "  0000000000000000000000000000000000000001" >>SKIP.exhaustive &&
+       echo "  $(test_oid 001)" >>SKIP.exhaustive &&
        git -c fsck.skipList=SKIP.exhaustive fsck 2>err &&
        test_must_be_empty err
 '
index 4b602826895e21154376391333c6517b4ec1ec54..566994cede3ef22c0c3927779a53d3923e3d6694 100755 (executable)
@@ -11,7 +11,7 @@ D=$(pwd)
 
 test_bundle_object_count () {
        git verify-pack -v "$1" >verify.out &&
-       test "$2" = $(grep '^[0-9a-f]\{40\} ' verify.out | wc -l)
+       test "$2" = $(grep "^$OID_REGEX " verify.out | wc -l)
 }
 
 convert_bundle_to_pack () {
@@ -174,6 +174,30 @@ test_expect_success 'fetch --prune --tags with refspec prunes based on refspec'
        git rev-parse sometag
 '
 
+test_expect_success '--refmap="" ignores configured refspec' '
+       cd "$TRASH_DIRECTORY" &&
+       git clone "$D" remote-refs &&
+       git -C remote-refs rev-parse remotes/origin/master >old &&
+       git -C remote-refs update-ref refs/remotes/origin/master master~1 &&
+       git -C remote-refs rev-parse remotes/origin/master >new &&
+       git -C remote-refs fetch --refmap= origin "+refs/heads/*:refs/hidden/origin/*" &&
+       git -C remote-refs rev-parse remotes/origin/master >actual &&
+       test_cmp new actual &&
+       git -C remote-refs fetch origin &&
+       git -C remote-refs rev-parse remotes/origin/master >actual &&
+       test_cmp old actual
+'
+
+test_expect_success '--refmap="" and --prune' '
+       git -C remote-refs update-ref refs/remotes/origin/foo/otherbranch master &&
+       git -C remote-refs update-ref refs/hidden/foo/otherbranch master &&
+       git -C remote-refs fetch --prune --refmap="" origin +refs/heads/*:refs/hidden/* &&
+       git -C remote-refs rev-parse remotes/origin/foo/otherbranch &&
+       test_must_fail git -C remote-refs rev-parse refs/hidden/foo/otherbranch &&
+       git -C remote-refs fetch --prune origin &&
+       test_must_fail git -C remote-refs rev-parse remotes/origin/foo/otherbranch
+'
+
 test_expect_success 'fetch tags when there is no tags' '
 
     cd "$D" &&
@@ -261,9 +285,10 @@ test_expect_success 'create bundle 1' '
 '
 
 test_expect_success 'header of bundle looks right' '
+       head -n 4 "$D"/bundle1 &&
        head -n 1 "$D"/bundle1 | grep "^#" &&
-       head -n 2 "$D"/bundle1 | grep "^-[0-9a-f]\{40\} " &&
-       head -n 3 "$D"/bundle1 | grep "^[0-9a-f]\{40\} " &&
+       head -n 2 "$D"/bundle1 | grep "^-$OID_REGEX " &&
+       head -n 3 "$D"/bundle1 | grep "^$OID_REGEX " &&
        head -n 4 "$D"/bundle1 | grep "^$"
 '
 
@@ -289,7 +314,7 @@ test_expect_success 'bundle 1 has only 3 files ' '
 test_expect_success 'unbundle 2' '
        cd "$D/bundle" &&
        git fetch ../bundle2 master:master &&
-       test "tip" = "$(git log -1 --pretty=oneline master | cut -b42-)"
+       test "tip" = "$(git log -1 --pretty=oneline master | cut -d" " -f2)"
 '
 
 test_expect_success 'bundle does not prerequisite objects' '
index d7b9f9078f6f95e866c6a0dec68511681f1957d6..62152bad1df68664229a5423e6a4bbec7c76e550 100755 (executable)
@@ -230,9 +230,10 @@ test_expect_success 'ls-remote --symref' '
 '
 
 test_expect_success 'ls-remote with filtered symref (refname)' '
-       cat >expect <<-\EOF &&
+       rev=$(git rev-parse HEAD) &&
+       cat >expect <<-EOF &&
        ref: refs/heads/master  HEAD
-       1bd44cb9d13204b0fe1958db0082f5028a16eb3a        HEAD
+       $rev    HEAD
        EOF
        # Protocol v2 supports sending symrefs for refs other than HEAD, so use
        # protocol v0 here.
@@ -242,10 +243,10 @@ test_expect_success 'ls-remote with filtered symref (refname)' '
 
 test_expect_failure 'ls-remote with filtered symref (--heads)' '
        git symbolic-ref refs/heads/foo refs/tags/mark &&
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
        ref: refs/tags/mark     refs/heads/foo
-       1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/foo
-       1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/master
+       $rev    refs/heads/foo
+       $rev    refs/heads/master
        EOF
        # Protocol v2 supports sending symrefs for refs other than HEAD, so use
        # protocol v0 here.
@@ -254,9 +255,9 @@ test_expect_failure 'ls-remote with filtered symref (--heads)' '
 '
 
 test_expect_success 'ls-remote --symref omits filtered-out matches' '
-       cat >expect <<-\EOF &&
-       1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/foo
-       1bd44cb9d13204b0fe1958db0082f5028a16eb3a        refs/heads/master
+       cat >expect <<-EOF &&
+       $rev    refs/heads/foo
+       $rev    refs/heads/master
        EOF
        # Protocol v2 supports sending symrefs for refs other than HEAD, so use
        # protocol v0 here.
index a1d3031d40dcc3d9234c3a5f37c4130c78f7f499..4ce9a9f7041112e49061acf14e0a51076ba770d2 100755 (executable)
@@ -14,7 +14,7 @@ corrupt_repo () {
 }
 
 test_expect_success 'setup and corrupt repository' '
-
+       test_oid_init &&
        echo file >file &&
        git add file &&
        git rev-parse :file &&
@@ -31,9 +31,10 @@ test_expect_success 'fsck fails' '
 '
 
 test_expect_success 'upload-pack fails due to error in pack-objects packing' '
-
-       printf "0032want %s\n00000009done\n0000" \
-               $(git rev-parse HEAD) >input &&
+       head=$(git rev-parse HEAD) &&
+       hexsz=$(test_oid hexsz) &&
+       printf "%04xwant %s\n00000009done\n0000" \
+               $(($hexsz + 10)) $head >input &&
        test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
        test_i18ngrep "unable to read" output.err &&
        test_i18ngrep "pack-objects died" output.err
@@ -51,16 +52,17 @@ test_expect_success 'fsck fails' '
 '
 test_expect_success 'upload-pack fails due to error in rev-list' '
 
-       printf "0032want %s\n0034shallow %s00000009done\n0000" \
-               $(git rev-parse HEAD) $(git rev-parse HEAD^) >input &&
+       printf "%04xwant %s\n%04xshallow %s00000009done\n0000" \
+               $(($hexsz + 10)) $(git rev-parse HEAD) \
+               $(($hexsz + 12)) $(git rev-parse HEAD^) >input &&
        test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
        grep "bad tree object" output.err
 '
 
 test_expect_success 'upload-pack fails due to bad want (no object)' '
 
-       printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
-               "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef" >input &&
+       printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \
+               $(($hexsz + 29)) $(test_oid deadbeef) >input &&
        test_must_fail git upload-pack . <input >output 2>output.err &&
        grep "not our ref" output.err &&
        grep "ERR" output &&
@@ -70,8 +72,8 @@ test_expect_success 'upload-pack fails due to bad want (no object)' '
 test_expect_success 'upload-pack fails due to bad want (not tip)' '
 
        oid=$(echo an object we have | git hash-object -w --stdin) &&
-       printf "0045want %s multi_ack_detailed\n00000009done\n0000" \
-               "$oid" >input &&
+       printf "%04xwant %s multi_ack_detailed\n00000009done\n0000" \
+               $(($hexsz + 29)) "$oid" >input &&
        test_must_fail git upload-pack . <input >output 2>output.err &&
        grep "not our ref" output.err &&
        grep "ERR" output &&
@@ -80,8 +82,8 @@ test_expect_success 'upload-pack fails due to bad want (not tip)' '
 
 test_expect_success 'upload-pack fails due to error in pack-objects enumeration' '
 
-       printf "0032want %s\n00000009done\n0000" \
-               $(git rev-parse HEAD) >input &&
+       printf "%04xwant %s\n00000009done\n0000" \
+               $((hexsz + 10)) $(git rev-parse HEAD) >input &&
        test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
        grep "bad tree object" output.err &&
        grep "pack-objects died" output.err
index 97a67728ca9b9a16f81fb278f44fd3412383c154..9e16512fe31b9bbb64c88093c5c317299a0e5735 100755 (executable)
@@ -15,7 +15,11 @@ test_expect_success 'setup' '
        commit 2 &&
        commit 3 &&
        commit 4 &&
-       git config --global transfer.fsckObjects true
+       git config --global transfer.fsckObjects true &&
+       test_oid_cache <<-EOF
+       sed sha1:s/0034shallow %s/0036unshallow %s/
+       sed sha256:s/004cshallow %s/004eunshallow %s/
+       EOF
 '
 
 test_expect_success 'setup shallow clone' '
@@ -239,7 +243,7 @@ test_expect_success 'shallow fetches check connectivity before writing shallow f
        # with an empty packfile. This is done by refetching with a shorter
        # depth (to ensure that the packfile is empty), and overwriting the
        # shallow line in the response with the unshallow line we want.
-       printf "s/0034shallow %s/0036unshallow %s/" \
+       printf "$(test_oid sed)" \
               "$(git -C "$REPO" rev-parse HEAD)" \
               "$(git -C "$REPO" rev-parse HEAD^)" \
               >"$HTTPD_ROOT_PATH/one-time-sed" &&
index a094fd5e71334bd16ac890234962485a7f9b7d24..d476c335098eef1e61aa8c7b8951fe760e8d13c1 100755 (executable)
@@ -134,15 +134,13 @@ test_expect_success 'MKCOL sends directory names with trailing slashes' '
 
 x1="[0-9a-f]"
 x2="$x1$x1"
-x5="$x1$x1$x1$x1$x1"
-x38="$x5$x5$x5$x5$x5$x5$x5$x1$x1$x1"
-x40="$x38$x2"
+xtrunc=$(echo $OID_REGEX | sed -e "s/\[0-9a-f\]\[0-9a-f\]//")
 
 test_expect_success 'PUT and MOVE sends object to URLs with SHA-1 hash suffix' '
        sed \
                -e "s/PUT /OP /" \
                -e "s/MOVE /OP /" \
-           -e "s|/objects/$x2/${x38}_$x40|WANTED_PATH_REQUEST|" \
+           -e "s|/objects/$x2/${xtrunc}_$OID_REGEX|WANTED_PATH_REQUEST|" \
                "$HTTPD_ROOT_PATH"/access.log |
        grep -e "\"OP .*WANTED_PATH_REQUEST HTTP/[.0-9]*\" 20[0-9] "
 
index f70cbcc9cabf6499de57529cbfa095ed9eff73e3..8f25f4b31fdcde9ee0b7321a87d7936181404cc2 100755 (executable)
@@ -107,7 +107,11 @@ test_expect_success 'use ref advertisement to filter out commits' '
 
        # The ref advertisement itself is filtered when protocol v2 is used, so
        # use v0.
-       GIT_TEST_PROTOCOL_VERSION= trace_fetch client origin to_fetch &&
+       (
+               GIT_TEST_PROTOCOL_VERSION= &&
+               export GIT_TEST_PROTOCOL_VERSION &&
+               trace_fetch client origin to_fetch
+       ) &&
        have_sent c5 c4^ c2side &&
        have_not_sent c4 c4^^ c4^^^
 '
@@ -169,7 +173,17 @@ test_expect_success 'do not send "have" with ancestors of commits that server AC
        test_commit -C server commit-on-b1 &&
 
        test_config -C client fetch.negotiationalgorithm skipping &&
-       trace_fetch client "$(pwd)/server" to_fetch &&
+
+       # NEEDSWORK: The number of "have"s sent depends on whether the transport
+       # is stateful. If the overspecification of the result were reduced, this
+       # test could be used for both stateful and stateless transports.
+       (
+               # Force protocol v0, in which local transport is stateful (in
+               # protocol v2 it is stateless).
+               GIT_TEST_PROTOCOL_VERSION=0 &&
+               export GIT_TEST_PROTOCOL_VERSION &&
+               trace_fetch client "$(pwd)/server" to_fetch
+       ) &&
        grep "  fetch" trace &&
 
        # fetch-pack sends 2 requests each containing 16 "have" lines before
index f0f425b2cf5746fb3f7205909cbc93eb25a84874..4a110b307ee53e623bff98a68307c4947139c39f 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success 'setup' '
        printf done | packetize >>fetch_body &&
        test_copy_bytes 10 <fetch_body >fetch_body.trunc &&
        hash_next=$(git commit-tree -p HEAD -m next HEAD^{tree}) &&
-       printf "%s %s refs/heads/newbranch\\0report-status\\n" "$_z40" "$hash_next" | packetize >push_body &&
+       printf "%s %s refs/heads/newbranch\\0report-status\\n" "$ZERO_OID" "$hash_next" | packetize >push_body &&
        printf 0000 >>push_body &&
        echo "$hash_next" | git pack-objects --stdout >>push_body &&
        test_copy_bytes 10 <push_body >push_body.trunc &&
index 3e9876e1971348daad7fd87f8aa107ea5fd6aaff..a53dd8550d0b8cbe40f49ece0381fb554ba87062 100755 (executable)
@@ -60,6 +60,27 @@ test_expect_success GPG 'pull commit with untrusted signature with --verify-sign
        test_i18ngrep "has an untrusted GPG signature" pullerror
 '
 
+test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=ultimate' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config gpg.minTrustLevel ultimate &&
+       test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
+       test_i18ngrep "has an untrusted GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=marginal' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config gpg.minTrustLevel marginal &&
+       test_must_fail git pull --ff-only --verify-signatures untrusted 2>pullerror &&
+       test_i18ngrep "has an untrusted GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit with untrusted signature with --verify-signatures and minTrustLevel=undefined' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config gpg.minTrustLevel undefined &&
+       git pull --ff-only --verify-signatures untrusted >pulloutput &&
+       test_i18ngrep "has a good GPG signature" pulloutput
+'
+
 test_expect_success GPG 'pull signed commit with --verify-signatures' '
        test_when_finished "git reset --hard && git checkout initial" &&
        git pull --verify-signatures signed >pulloutput &&
@@ -79,10 +100,53 @@ test_expect_success GPG 'pull commit with bad signature with --no-verify-signatu
 '
 
 test_expect_success GPG 'pull unsigned commit into unborn branch' '
+       test_when_finished "rm -rf empty-repo" &&
        git init empty-repo &&
        test_must_fail \
                git -C empty-repo pull --verify-signatures ..  2>pullerror &&
        test_i18ngrep "does not have a GPG signature" pullerror
 '
 
+test_expect_success GPG 'pull commit into unborn branch with bad signature and --verify-signatures' '
+       test_when_finished "rm -rf empty-repo" &&
+       git init empty-repo &&
+       test_must_fail \
+               git -C empty-repo pull --ff-only --verify-signatures ../bad 2>pullerror &&
+       test_i18ngrep "has a bad GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures' '
+       test_when_finished "rm -rf empty-repo" &&
+       git init empty-repo &&
+       test_must_fail \
+               git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
+       test_i18ngrep "has an untrusted GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=ultimate' '
+       test_when_finished "rm -rf empty-repo" &&
+       git init empty-repo &&
+       test_config_global gpg.minTrustLevel ultimate &&
+       test_must_fail \
+               git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
+       test_i18ngrep "has an untrusted GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=marginal' '
+       test_when_finished "rm -rf empty-repo" &&
+       git init empty-repo &&
+       test_config_global gpg.minTrustLevel marginal &&
+       test_must_fail \
+               git -C empty-repo pull --ff-only --verify-signatures ../untrusted 2>pullerror &&
+       test_i18ngrep "has an untrusted GPG signature" pullerror
+'
+
+test_expect_success GPG 'pull commit into unborn branch with untrusted signature and --verify-signatures and minTrustLevel=undefined' '
+       test_when_finished "rm -rf empty-repo" &&
+       git init empty-repo &&
+       test_config_global gpg.minTrustLevel undefined &&
+       git -C empty-repo pull --ff-only --verify-signatures ../untrusted >pulloutput &&
+       test_i18ngrep "has a good GPG signature" pulloutput
+'
+
 test_done
index ad8c41176ef76690004c8f73131a7ba464758143..84ea2a3eb707a7405d7fc3c5267f8bfff06abd18 100755 (executable)
@@ -635,10 +635,10 @@ partial_clone_server () {
        rm -rf "$SERVER" client &&
        test_create_repo "$SERVER" &&
        test_commit -C "$SERVER" one &&
-       HASH1=$(git hash-object "$SERVER/one.t") &&
+       HASH1=$(git -C "$SERVER" hash-object one.t) &&
        git -C "$SERVER" revert HEAD &&
        test_commit -C "$SERVER" two &&
-       HASH2=$(git hash-object "$SERVER/two.t") &&
+       HASH2=$(git -C "$SERVER" hash-object two.t) &&
        test_config -C "$SERVER" uploadpack.allowfilter 1 &&
        test_config -C "$SERVER" uploadpack.allowanysha1inwant 1
 }
index 4894237ab8059c83aea8aae50a0803ebf9df6831..0c74b4e21a3ef17499401cdd1f4eeaab5c392137 100755 (executable)
@@ -326,15 +326,16 @@ test_expect_success SYMLINKS 'clone repo with symlinked or unknown files at obje
        for raw in $(ls T*.raw)
        do
                sed -e "s!/../!/Y/!; s![0-9a-f]\{38,\}!Z!" -e "/commit-graph/d" \
-                   -e "/multi-pack-index/d" <$raw >$raw.de-sha || return 1
+                   -e "/multi-pack-index/d" <$raw >$raw.de-sha-1 &&
+               sort $raw.de-sha-1 >$raw.de-sha || return 1
        done &&
 
        cat >expected-files <<-EOF &&
        ./Y/Z
        ./Y/Z
+       ./Y/Z
        ./a-loose-dir/Z
        ./an-object
-       ./Y/Z
        ./info/packs
        ./pack/pack-Z.idx
        ./pack/pack-Z.pack
index fea56cda6d3a256161a21cb7e79e6e96f328bf17..9a9178fd2810025322d1ad3048a09a661eaac110 100755 (executable)
@@ -309,26 +309,36 @@ setup_triangle () {
 
        printf "line %d\n" $(test_seq 1 100) >big-blob.txt &&
 
-       # Create a server with 2 commits: a commit with a big blob and a child
+       # Create a server with 2 commits: a commit with a big tree and a child
        # commit with an incremental change. Also, create a partial clone
        # client that only contains the first commit.
        git init server &&
        git -C server config --local uploadpack.allowfilter 1 &&
-       cp big-blob.txt server &&
-       git -C server add big-blob.txt &&
+       for i in $(test_seq 1 100)
+       do
+               echo "make the tree big" >server/file$i &&
+               git -C server add file$i
+       done &&
        git -C server commit -m "initial" &&
        git clone --bare --filter=tree:0 "file://$(pwd)/server" client &&
-       echo another line >>server/big-blob.txt &&
-       git -C server commit -am "append line to big blob" &&
+       echo another line >>server/file1 &&
+       git -C server commit -am "incremental change" &&
 
-       # Create a promisor remote that only contains the blob from the first
-       # commit, and set it as the promisor remote of client. Thus, whenever
-       # the client lazy fetches, the lazy fetch will succeed only if it is
-       # for this blob.
+       # Create a promisor remote that only contains the tree and blob from
+       # the first commit.
        git init promisor-remote &&
+       git -C server config --local uploadpack.allowanysha1inwant 1 &&
+       TREE_HASH=$(git -C server rev-parse HEAD~1^{tree}) &&
+       git -C promisor-remote fetch --keep "file://$(pwd)/server" "$TREE_HASH" &&
+       git -C promisor-remote count-objects -v >object-count &&
+       test_i18ngrep "count: 0" object-count &&
+       test_i18ngrep "in-pack: 2" object-count &&
+
+       # Set it as the promisor remote of client. Thus, whenever
+       # the client lazy fetches, the lazy fetch will succeed only if it is
+       # for this tree or blob.
        test_commit -C promisor-remote one && # so that ref advertisement is not empty
        git -C promisor-remote config --local uploadpack.allowanysha1inwant 1 &&
-       git -C promisor-remote hash-object -w --stdin <big-blob.txt &&
        git -C client remote set-url origin "file://$(pwd)/promisor-remote"
 }
 
@@ -341,14 +351,14 @@ test_expect_success 'fetch lazy-fetches only to resolve deltas' '
        setup_triangle &&
 
        # Exercise to make sure it works. Git will not fetch anything from the
-       # promisor remote other than for the big blob (because it needs to
+       # promisor remote other than for the big tree (because it needs to
        # resolve the delta).
        GIT_TRACE_PACKET="$(pwd)/trace" git -C client \
                fetch "file://$(pwd)/server" master &&
 
        # Verify the assumption that the client needed to fetch the delta base
        # to resolve the delta.
-       git hash-object big-blob.txt >hash &&
+       git -C server rev-parse HEAD~1^{tree} >hash &&
        grep "want $(cat hash)" trace
 '
 
@@ -370,7 +380,7 @@ test_expect_success 'fetch lazy-fetches only to resolve deltas, protocol v2' '
 
        # Verify the assumption that the client needed to fetch the delta base
        # to resolve the delta.
-       git hash-object big-blob.txt >hash &&
+       git -C server rev-parse HEAD~1^{tree} >hash &&
        grep "want $(cat hash)" trace
 '
 
index e73067d23fe747210f0ea767a7e8952b81ed3d30..7fd7102c8741f3a6c426d3f84b6556b9bbe9a861 100755 (executable)
@@ -665,6 +665,18 @@ test_expect_success 'fetch from namespaced repo respects namespaces' '
        test_cmp expect actual
 '
 
+test_expect_success 'ls-remote with v2 http sends only one POST' '
+       test_when_finished "rm -f log" &&
+
+       git ls-remote "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" >expect &&
+       GIT_TRACE_CURL="$(pwd)/log" git -c protocol.version=2 \
+               ls-remote "$HTTPD_URL/smart/http_parent" >actual &&
+       test_cmp expect actual &&
+
+       grep "Send header: POST" log >posts &&
+       test_line_count = 1 posts
+'
+
 test_expect_success 'push with http:// and a config of v2 does not request v2' '
        test_when_finished "rm -f log" &&
        # Till v2 for push is designed, make sure that if a client has
index 433c4de08f0cc8d220d5368ab2ab0dffde372482..6c0a90d044c52500fe1b1a31d2bf12eef9691de0 100755 (executable)
@@ -10,52 +10,53 @@ if core.symlinks is false.'
 
 . ./test-lib.sh
 
-test_expect_success \
-'setup' '
-git config core.symlinks false &&
-> file &&
-git add file &&
-git commit -m initial &&
-git branch b-symlink &&
-git branch b-file &&
-l=$(printf file | git hash-object -t blob -w --stdin) &&
-echo "120000 $l        symlink" | git update-index --index-info &&
-git commit -m master &&
-git checkout b-symlink &&
-l=$(printf file-different | git hash-object -t blob -w --stdin) &&
-echo "120000 $l        symlink" | git update-index --index-info &&
-git commit -m b-symlink &&
-git checkout b-file &&
-echo plain-file > symlink &&
-git add symlink &&
-git commit -m b-file'
-
-test_expect_success \
-'merge master into b-symlink, which has a different symbolic link' '
-git checkout b-symlink &&
-test_must_fail git merge master'
-
-test_expect_success \
-'the merge result must be a file' '
-test -f symlink'
-
-test_expect_success \
-'merge master into b-file, which has a file instead of a symbolic link' '
-git reset --hard && git checkout b-file &&
-test_must_fail git merge master'
-
-test_expect_success \
-'the merge result must be a file' '
-test -f symlink'
-
-test_expect_success \
-'merge b-file, which has a file instead of a symbolic link, into master' '
-git reset --hard &&
-git checkout master &&
-test_must_fail git merge b-file'
-
-test_expect_success \
-'the merge result must be a file' '
-test -f symlink'
+test_expect_success 'setup' '
+       git config core.symlinks false &&
+       >file &&
+       git add file &&
+       git commit -m initial &&
+       git branch b-symlink &&
+       git branch b-file &&
+       l=$(printf file | git hash-object -t blob -w --stdin) &&
+       echo "120000 $l symlink" | git update-index --index-info &&
+       git commit -m master &&
+       git checkout b-symlink &&
+       l=$(printf file-different | git hash-object -t blob -w --stdin) &&
+       echo "120000 $l symlink" | git update-index --index-info &&
+       git commit -m b-symlink &&
+       git checkout b-file &&
+       echo plain-file >symlink &&
+       git add symlink &&
+       git commit -m b-file
+'
+
+test_expect_success 'merge master into b-symlink, which has a different symbolic link' '
+       git checkout b-symlink &&
+       test_must_fail git merge master
+'
+
+test_expect_success 'the merge result must be a file' '
+       test_path_is_file symlink
+'
+
+test_expect_success 'merge master into b-file, which has a file instead of a symbolic link' '
+       git reset --hard &&
+       git checkout b-file &&
+       test_must_fail git merge master
+'
+
+test_expect_success 'the merge result must be a file' '
+       test_path_is_file symlink
+'
+
+test_expect_success 'merge b-file, which has a file instead of a symbolic link, into master' '
+       git reset --hard &&
+       git checkout master &&
+       test_must_fail git merge b-file
+'
+
+test_expect_success 'the merge result must be a file' '
+       test_path_is_file symlink
+'
 
 test_done
index 8f077bea6095a6fea8d92ac8a83b11f5d865cdf6..5c5bc32ccb13ac77b453a90e432c74f3ed331ac1 100755 (executable)
@@ -86,6 +86,30 @@ test_expect_success GPGSM 'verify and show signatures x509' '
        echo ninth-signed-x509 OK
 '
 
+test_expect_success GPGSM 'verify and show signatures x509 with low minTrustLevel' '
+       test_config gpg.minTrustLevel undefined &&
+       git verify-tag ninth-signed-x509 2>actual &&
+       grep "Good signature from" actual &&
+       ! grep "BAD signature from" actual &&
+       echo ninth-signed-x509 OK
+'
+
+test_expect_success GPGSM 'verify and show signatures x509 with matching minTrustLevel' '
+       test_config gpg.minTrustLevel fully &&
+       git verify-tag ninth-signed-x509 2>actual &&
+       grep "Good signature from" actual &&
+       ! grep "BAD signature from" actual &&
+       echo ninth-signed-x509 OK
+'
+
+test_expect_success GPGSM 'verify and show signatures x509 with high minTrustLevel' '
+       test_config gpg.minTrustLevel ultimate &&
+       test_must_fail git verify-tag ninth-signed-x509 2>actual &&
+       grep "Good signature from" actual &&
+       ! grep "BAD signature from" actual &&
+       echo ninth-signed-x509 OK
+'
+
 test_expect_success GPG 'detect fudged signature' '
        git cat-file tag seventh-signed >raw &&
        sed -e "/^tag / s/seventh/7th forged/" raw >forged1 &&
index 6b1a731fffe65f10259a5200e071c4767181d759..cad3a9de9efc21339d82b4532d5af482e3446bc9 100755 (executable)
@@ -105,8 +105,12 @@ test_expect_success 'CRLF delimiters' '
 test_expect_success 'quotes' '
        restore_checkpoint &&
 
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
        git rm fileA.t &&
-       printf "\"file\\101.t\"" | git reset --pathspec-from-file=- &&
+       git reset --pathspec-from-file=list &&
 
        cat >expect <<-\EOF &&
         D fileA.t
@@ -117,8 +121,10 @@ test_expect_success 'quotes' '
 test_expect_success 'quotes not compatible with --pathspec-file-nul' '
        restore_checkpoint &&
 
-       git rm fileA.t &&
-       printf "\"file\\101.t\"" >list &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
        # Note: "git reset" has not yet learned to fail on wrong pathspecs
        git reset --pathspec-from-file=list --pathspec-file-nul &&
 
@@ -128,15 +134,6 @@ test_expect_success 'quotes not compatible with --pathspec-file-nul' '
        test_must_fail verify_expect
 '
 
-test_expect_success '--pathspec-from-file is not compatible with --soft or --hard' '
-       restore_checkpoint &&
-
-       git rm fileA.t &&
-       echo fileA.t >list &&
-       test_must_fail git reset --soft --pathspec-from-file=list &&
-       test_must_fail git reset --hard --pathspec-from-file=list
-'
-
 test_expect_success 'only touches what was listed' '
        restore_checkpoint &&
 
@@ -152,4 +149,25 @@ test_expect_success 'only touches what was listed' '
        verify_expect
 '
 
+test_expect_success 'error conditions' '
+       restore_checkpoint &&
+       echo fileA.t >list &&
+       git rm fileA.t &&
+
+       test_must_fail git reset --pathspec-from-file=list --patch 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --patch" err &&
+
+       test_must_fail git reset --pathspec-from-file=list -- fileA.t 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+       test_must_fail git reset --pathspec-file-nul 2>err &&
+       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+
+       test_must_fail git reset --soft --pathspec-from-file=list 2>err &&
+       test_i18ngrep -e "fatal: Cannot do soft reset with paths" err &&
+
+       test_must_fail git reset --hard --pathspec-from-file=list 2>err &&
+       test_i18ngrep -e "fatal: Cannot do hard reset with paths" err
+'
+
 test_done
index 6e6d24c1c3a5c5397241e266c09fe214ec14d53f..cb5e34d94c3a163ecc3785d4b3afdeeb98c60ec6 100755 (executable)
@@ -737,4 +737,13 @@ test_expect_success MINGW 'handle clean & core.longpaths = false nicely' '
        test_i18ngrep "too long" .git/err
 '
 
+test_expect_success 'clean untracked paths by pathspec' '
+       git init untracked &&
+       mkdir untracked/dir &&
+       echo >untracked/dir/file.txt &&
+       git -C untracked clean -f dir/file.txt &&
+       ls untracked/dir >actual &&
+       test_must_be_empty actual
+'
+
 test_done
index 7f75bb1be62dc8dfb223e4e89ad3a2faf07bb50a..42a00f95b9df42362f1b8afe7eff9ca58d9d4d06 100755 (executable)
@@ -156,9 +156,9 @@ test_expect_success 'submodule add to .gitignored path fails' '
        (
                cd addtest-ignore &&
                cat <<-\EOF >expect &&
-               The following path is ignored by one of your .gitignore files:
+               The following paths are ignored by one of your .gitignore files:
                submod
-               Use -f if you really want to add it.
+               Use -f if you really want to add them.
                EOF
                # Does not use test_commit due to the ignore
                echo "*" > .gitignore &&
@@ -191,6 +191,17 @@ test_expect_success 'submodule add to reconfigure existing submodule with --forc
        )
 '
 
+test_expect_success 'submodule add relays add --dry-run stderr' '
+       test_when_finished "rm -rf addtest/.git/index.lock" &&
+       (
+               cd addtest &&
+               : >.git/index.lock &&
+               ! git submodule add "$submodurl" sub-while-locked 2>output.err &&
+               test_i18ngrep "^fatal: .*index\.lock" output.err &&
+               test_path_is_missing sub-while-locked
+       )
+'
+
 test_expect_success 'submodule add --branch' '
        echo "refs/heads/initial" >expect-head &&
        cat <<-\EOF >expect-heads &&
diff --git a/t/t7410-submodule-checkout-to.sh b/t/t7410-submodule-checkout-to.sh
deleted file mode 100755 (executable)
index f1b492e..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/bin/sh
-
-test_description='Combination of submodules and multiple workdirs'
-
-. ./test-lib.sh
-
-base_path=$(pwd -P)
-
-test_expect_success 'setup: make origin'  '
-       mkdir -p origin/sub &&
-       (
-               cd origin/sub && git init &&
-               echo file1 >file1 &&
-               git add file1 &&
-               git commit -m file1
-       ) &&
-       mkdir -p origin/main &&
-       (
-               cd origin/main && git init &&
-               git submodule add ../sub &&
-               git commit -m "add sub"
-       ) &&
-       (
-               cd origin/sub &&
-               echo file1updated >file1 &&
-               git add file1 &&
-               git commit -m "file1 updated"
-       ) &&
-       git -C origin/main/sub pull &&
-       (
-               cd origin/main &&
-               git add sub &&
-               git commit -m "sub updated"
-       )
-'
-
-test_expect_success 'setup: clone' '
-       mkdir clone &&
-       git -C clone clone --recursive "$base_path/origin/main"
-'
-
-rev1_hash_main=$(git --git-dir=origin/main/.git show --pretty=format:%h -q "HEAD~1")
-rev1_hash_sub=$(git --git-dir=origin/sub/.git show --pretty=format:%h -q "HEAD~1")
-
-test_expect_success 'checkout main' '
-       mkdir default_checkout &&
-       git -C clone/main worktree add "$base_path/default_checkout/main" "$rev1_hash_main"
-'
-
-test_expect_failure 'can see submodule diffs just after checkout' '
-       git -C default_checkout/main diff --submodule master"^!" >out &&
-       grep "file1 updated" out
-'
-
-test_expect_success 'checkout main and initialize independent clones' '
-       mkdir fully_cloned_submodule &&
-       git -C clone/main worktree add "$base_path/fully_cloned_submodule/main" "$rev1_hash_main" &&
-       git -C fully_cloned_submodule/main submodule update
-'
-
-test_expect_success 'can see submodule diffs after independent cloning' '
-       git -C fully_cloned_submodule/main diff --submodule master"^!" >out &&
-       grep "file1 updated" out
-'
-
-test_expect_success 'checkout sub manually' '
-       mkdir linked_submodule &&
-       git -C clone/main worktree add "$base_path/linked_submodule/main" "$rev1_hash_main" &&
-       git -C clone/main/sub worktree add "$base_path/linked_submodule/main/sub" "$rev1_hash_sub"
-'
-
-test_expect_success 'can see submodule diffs after manual checkout of linked submodule' '
-       git -C linked_submodule/main diff --submodule master"^!" >out &&
-       grep "file1 updated" out
-'
-
-test_done
index 46a5cd4b7395ce8bba29763644a6fbd6739c929e..6d19ece05dd320b970492daf015b874eee0d391f 100755 (executable)
@@ -382,4 +382,13 @@ test_expect_success 'check commit with unstaged rename and copy' '
        )
 '
 
+test_expect_success 'commit without staging files fails and displays hints' '
+       echo "initial" >file &&
+       git add file &&
+       git commit -m initial &&
+       echo "changes" >>file &&
+       test_must_fail git commit -m update >actual &&
+       test_i18ngrep "no changes added to commit (use \"git add\" and/or \"git commit -a\")" actual
+'
+
 test_done
index 682b23a06818cddf6ff89e11b4395d32cebe9758..0c06d22a0079a61f04cd83dad29562dc4d017218 100755 (executable)
@@ -109,6 +109,21 @@ test_expect_success GPG 'verify-commit exits success on untrusted signature' '
        grep "not certified" actual
 '
 
+test_expect_success GPG 'verify-commit exits success with matching minTrustLevel' '
+       test_config gpg.minTrustLevel ultimate &&
+       git verify-commit sixth-signed
+'
+
+test_expect_success GPG 'verify-commit exits success with low minTrustLevel' '
+       test_config gpg.minTrustLevel fully &&
+       git verify-commit sixth-signed
+'
+
+test_expect_success GPG 'verify-commit exits failure with high minTrustLevel' '
+       test_config gpg.minTrustLevel ultimate &&
+       test_must_fail git verify-commit eighth-signed-alt
+'
+
 test_expect_success GPG 'verify signatures with --raw' '
        (
                for commit in initial second merge fourth-signed fifth-signed sixth-signed seventh-signed
@@ -219,6 +234,30 @@ test_expect_success GPG 'show untrusted signature with custom format' '
        test_cmp expect actual
 '
 
+test_expect_success GPG 'show untrusted signature with undefined trust level' '
+       cat >expect <<-\EOF &&
+       undefined
+       65A0EEA02E30CAD7
+       Eris Discordia <discord@example.net>
+       F8364A59E07FFE9F4D63005A65A0EEA02E30CAD7
+       D4BE22311AD3131E5EDA29A461092E85B7227189
+       EOF
+       git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" eighth-signed-alt >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success GPG 'show untrusted signature with ultimate trust level' '
+       cat >expect <<-\EOF &&
+       ultimate
+       13B6F51ECDDE430D
+       C O Mitter <committer@example.com>
+       73D758744BE721698EC54E8713B6F51ECDDE430D
+       73D758744BE721698EC54E8713B6F51ECDDE430D
+       EOF
+       git log -1 --format="%GT%n%GK%n%GS%n%GF%n%GP" sixth-signed >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success GPG 'show unknown signature with custom format' '
        cat >expect <<-\EOF &&
        E
index 4b58901ed67c1349b255dd54977692f36cac988c..5fbe47ebcd02714b8bf2e0cdf5f0d32064b55fd8 100755 (executable)
@@ -100,7 +100,11 @@ test_expect_success 'CRLF delimiters' '
 test_expect_success 'quotes' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" | git commit --pathspec-from-file=- -m "Commit" &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
+       git commit --pathspec-from-file=list -m "Commit" &&
 
        cat >expect <<-\EOF &&
        A       fileA.t
@@ -111,7 +115,10 @@ test_expect_success 'quotes' '
 test_expect_success 'quotes not compatible with --pathspec-file-nul' '
        restore_checkpoint &&
 
-       printf "\"file\\101.t\"" >list &&
+       cat >list <<-\EOF &&
+       "file\101.t"
+       EOF
+
        test_must_fail git commit --pathspec-from-file=list --pathspec-file-nul -m "Commit"
 '
 
@@ -127,10 +134,31 @@ test_expect_success 'only touches what was listed' '
        verify_expect
 '
 
-test_expect_success '--pathspec-from-file and --all cannot be used together' '
+test_expect_success 'error conditions' '
        restore_checkpoint &&
-       test_must_fail git commit --pathspec-from-file=- --all -m "Commit" 2>err &&
-       test_i18ngrep "[-]-pathspec-from-file with -a does not make sense" err
+       echo fileA.t >list &&
+       >empty_list &&
+
+       test_must_fail git commit --pathspec-from-file=list --interactive -m "Commit" 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+
+       test_must_fail git commit --pathspec-from-file=list --patch -m "Commit" 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with --interactive/--patch" err &&
+
+       test_must_fail git commit --pathspec-from-file=list --all -m "Commit" 2>err &&
+       test_i18ngrep -e "--pathspec-from-file with -a does not make sense" err &&
+
+       test_must_fail git commit --pathspec-from-file=list -m "Commit" -- fileA.t 2>err &&
+       test_i18ngrep -e "--pathspec-from-file is incompatible with pathspec arguments" err &&
+
+       test_must_fail git commit --pathspec-file-nul -m "Commit" 2>err &&
+       test_i18ngrep -e "--pathspec-file-nul requires --pathspec-from-file" err &&
+
+       test_must_fail git commit --pathspec-from-file=empty_list --include -m "Commit" 2>err &&
+       test_i18ngrep -e "No paths with --include/--only does not make sense." err &&
+
+       test_must_fail git commit --pathspec-from-file=empty_list --only -m "Commit" 2>err &&
+       test_i18ngrep -e "No paths with --include/--only does not make sense." err
 '
 
 test_done
index d99218a725c5956d75e32684b196bdc050a0e8b1..a426f3a89aa76fb9b89464988211264e56314a60 100755 (executable)
@@ -66,6 +66,20 @@ test_expect_success GPG 'merge commit with untrusted signature with verification
        test_i18ngrep "has an untrusted GPG signature" mergeerror
 '
 
+test_expect_success GPG 'merge commit with untrusted signature with verification and high minTrustLevel' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config gpg.minTrustLevel marginal &&
+       test_must_fail git merge --ff-only --verify-signatures side-untrusted 2>mergeerror &&
+       test_i18ngrep "has an untrusted GPG signature" mergeerror
+'
+
+test_expect_success GPG 'merge commit with untrusted signature with verification and low minTrustLevel' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config gpg.minTrustLevel undefined &&
+       git merge --ff-only --verify-signatures side-untrusted >mergeoutput &&
+       test_i18ngrep "has a good GPG signature" mergeoutput
+'
+
 test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true' '
        test_when_finished "git reset --hard && git checkout initial" &&
        test_config merge.verifySignatures true &&
@@ -73,6 +87,14 @@ test_expect_success GPG 'merge commit with untrusted signature with merge.verify
        test_i18ngrep "has an untrusted GPG signature" mergeerror
 '
 
+test_expect_success GPG 'merge commit with untrusted signature with merge.verifySignatures=true and minTrustLevel' '
+       test_when_finished "git reset --hard && git checkout initial" &&
+       test_config merge.verifySignatures true &&
+       test_config gpg.minTrustLevel marginal &&
+       test_must_fail git merge --ff-only side-untrusted 2>mergeerror &&
+       test_i18ngrep "has an untrusted GPG signature" mergeerror
+'
+
 test_expect_success GPG 'merge signed commit with verification' '
        test_when_finished "git reset --hard && git checkout initial" &&
        git merge --verbose --ff-only --verify-signatures side-signed >mergeoutput &&
index 6bac9ed180e7342b34401b2d6af9e83b4472f08d..29b92907e2ad850514fe970ca1adeeda8d0697c8 100755 (executable)
@@ -125,15 +125,14 @@ test_expect_success 'difftool stops on error with --trust-exit-code' '
        test_when_finished "rm -f for-diff .git/fail-right-file" &&
        test_when_finished "git reset -- for-diff" &&
        write_script .git/fail-right-file <<-\EOF &&
-       echo "$2"
+       echo failed
        exit 1
        EOF
        >for-diff &&
        git add for-diff &&
-       echo file >expect &&
        test_must_fail git difftool -y --trust-exit-code \
                --extcmd .git/fail-right-file branch >actual &&
-       test_cmp expect actual
+       test_line_count = 1 actual
 '
 
 test_expect_success 'difftool honors exit status if command not found' '
index 93877ba9cd625de373cff3a704b17c9bdf7ed379..5505e5aa249e43b88455b0565f0f9348546d5e4c 100755 (executable)
@@ -1363,6 +1363,63 @@ test_expect_success 'teardown after path completion tests' '
               BS\\dir '$'separators\034in\035dir''
 '
 
+test_expect_success '__git_find_on_cmdline - single match' '
+       echo list >expect &&
+       (
+               words=(git command --opt list) &&
+               cword=${#words[@]} &&
+               __git_find_on_cmdline "add list remove" >actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success '__git_find_on_cmdline - multiple matches' '
+       echo remove >expect &&
+       (
+               words=(git command -o --opt remove list add) &&
+               cword=${#words[@]} &&
+               __git_find_on_cmdline "add list remove" >actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success '__git_find_on_cmdline - no match' '
+       (
+               words=(git command --opt branch) &&
+               cword=${#words[@]} &&
+               __git_find_on_cmdline "add list remove" >actual
+       ) &&
+       test_must_be_empty actual
+'
+
+test_expect_success '__git_find_on_cmdline - single match with index' '
+       echo "3 list" >expect &&
+       (
+               words=(git command --opt list) &&
+               cword=${#words[@]} &&
+               __git_find_on_cmdline --show-idx "add list remove" >actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success '__git_find_on_cmdline - multiple matches with index' '
+       echo "4 remove" >expect &&
+       (
+               words=(git command -o --opt remove list add) &&
+               cword=${#words[@]} &&
+               __git_find_on_cmdline --show-idx "add list remove" >actual
+       ) &&
+       test_cmp expect actual
+'
+
+test_expect_success '__git_find_on_cmdline - no match with index' '
+       (
+               words=(git command --opt branch) &&
+               cword=${#words[@]} &&
+               __git_find_on_cmdline --show-idx "add list remove" >actual
+       ) &&
+       test_must_be_empty actual
+'
 
 test_expect_success '__git_get_config_variables' '
        cat >expect <<-EOF &&
index 6a756416384c210ada2631f17862f5c01fffa478..e144712c85c055bcf3248ab342592b440a477062 100755 (executable)
@@ -16,7 +16,7 @@ else
 fi
 
 # If you want to allow non-ASCII filenames set this variable to true.
-allownonascii=$(git config --bool hooks.allownonascii)
+allownonascii=$(git config --type=bool hooks.allownonascii)
 
 # Redirect output to stderr.
 exec 1>&2
index 80ba94135cc378364af9d3cb2450df48e51faf2c..5014c4b31cb9700dbd458c8841d8cd91d3b8773c 100755 (executable)
@@ -43,11 +43,11 @@ if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
 fi
 
 # --- Config
-allowunannotated=$(git config --bool hooks.allowunannotated)
-allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
-denycreatebranch=$(git config --bool hooks.denycreatebranch)
-allowdeletetag=$(git config --bool hooks.allowdeletetag)
-allowmodifytag=$(git config --bool hooks.allowmodifytag)
+allowunannotated=$(git config --type=bool hooks.allowunannotated)
+allowdeletebranch=$(git config --type=bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --type=bool hooks.denycreatebranch)
+allowdeletetag=$(git config --type=bool hooks.allowdeletetag)
+allowmodifytag=$(git config --type=bool hooks.allowmodifytag)
 
 # check for no description
 projectdesc=$(sed -e '1q' "$GIT_DIR/description")
index 83379a037d69c2f6d97004d41b96f82f10d209dc..1fdc7dac1a6230bdf3492cbebd6642fc825dac5f 100644 (file)
@@ -737,7 +737,7 @@ static int disconnect_git(struct transport *transport)
 {
        struct git_transport_data *data = transport->data;
        if (data->conn) {
-               if (data->got_remote_heads)
+               if (data->got_remote_heads && !transport->stateless_rpc)
                        packet_flush(data->fd[1]);
                close(data->fd[0]);
                close(data->fd[1]);
index b4a9d97f25b49f1a29dd6862803192684c02c50a..da4d6d4ec01d6ca96e9efb08e828e8694567cd9f 100644 (file)
@@ -372,15 +372,20 @@ static int check_updates(struct unpack_trees_options *o)
        state.refresh_cache = 1;
        state.istate = index;
 
+       if (!o->update || o->dry_run) {
+               remove_marked_cache_entries(index, 0);
+               trace_performance_leave("check_updates");
+               return 0;
+       }
+
        if (o->clone)
                setup_collided_checkout_detection(&state, index);
 
        progress = get_progress(o);
 
-       if (o->update)
-               git_attr_set_direction(GIT_ATTR_CHECKOUT);
+       git_attr_set_direction(GIT_ATTR_CHECKOUT);
 
-       if (should_update_submodules() && o->update && !o->dry_run)
+       if (should_update_submodules())
                load_gitmodules_file(index, NULL);
 
        for (i = 0; i < index->cache_nr; i++) {
@@ -388,18 +393,18 @@ static int check_updates(struct unpack_trees_options *o)
 
                if (ce->ce_flags & CE_WT_REMOVE) {
                        display_progress(progress, ++cnt);
-                       if (o->update && !o->dry_run)
-                               unlink_entry(ce);
+                       unlink_entry(ce);
                }
        }
+
        remove_marked_cache_entries(index, 0);
        remove_scheduled_dirs();
 
-       if (should_update_submodules() && o->update && !o->dry_run)
+       if (should_update_submodules())
                load_gitmodules_file(index, &state);
 
        enable_delayed_checkout(&state);
-       if (has_promisor_remote() && o->update && !o->dry_run) {
+       if (has_promisor_remote()) {
                /*
                 * Prefetch the objects that are to be checked out in the loop
                 * below.
@@ -431,15 +436,12 @@ static int check_updates(struct unpack_trees_options *o)
                                    ce->name);
                        display_progress(progress, ++cnt);
                        ce->ce_flags &= ~CE_UPDATE;
-                       if (o->update && !o->dry_run) {
-                               errs |= checkout_entry(ce, &state, NULL, NULL);
-                       }
+                       errs |= checkout_entry(ce, &state, NULL, NULL);
                }
        }
        stop_progress(&progress);
        errs |= finish_delayed_checkout(&state, NULL);
-       if (o->update)
-               git_attr_set_direction(GIT_ATTR_CHECKIN);
+       git_attr_set_direction(GIT_ATTR_CHECKIN);
 
        if (o->clone)
                report_collided_checkout(index);
@@ -694,9 +696,11 @@ static int index_pos_by_traverse_info(struct name_entry *names,
        if (pos >= 0)
                BUG("This is a directory and should not exist in index");
        pos = -pos - 1;
-       if (!starts_with(o->src_index->cache[pos]->name, name.buf) ||
+       if (pos >= o->src_index->cache_nr ||
+           !starts_with(o->src_index->cache[pos]->name, name.buf) ||
            (pos > 0 && starts_with(o->src_index->cache[pos-1]->name, name.buf)))
-               BUG("pos must point at the first entry in this directory");
+               BUG("pos %d doesn't point to the first entry of %s in index",
+                   pos, name.buf);
        strbuf_release(&name);
        return pos;
 }
@@ -1305,14 +1309,14 @@ static int clear_ce_flags_dir(struct index_state *istate,
 
        if (pl->use_cone_patterns && orig_ret == MATCHED_RECURSIVE) {
                struct cache_entry **ce = cache;
-               rc = (cache_end - cache) / sizeof(struct cache_entry *);
+               rc = cache_end - cache;
 
                while (ce < cache_end) {
                        (*ce)->ce_flags &= ~clear_mask;
                        ce++;
                }
        } else if (pl->use_cone_patterns && orig_ret == NOT_MATCHED) {
-               rc = (cache_end - cache) / sizeof(struct cache_entry *);
+               rc = cache_end - cache;
        } else {
                rc = clear_ce_flags_1(istate, cache, cache_end - cache,
                                      prefix,