]> git.ipfire.org Git - thirdparty/git.git/commitdiff
Merge branch 'jc/diffcore-rotate'
authorJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2021 00:43:30 +0000 (16:43 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 26 Feb 2021 00:43:30 +0000 (16:43 -0800)
"git {diff,log} --{skip,rotate}-to=<path>" allows the user to
discard diff output for early paths or move them to the end of the
output.

* jc/diffcore-rotate:
  diff: --{rotate,skip}-to=<path>

307 files changed:
Documentation/RelNotes/2.31.0.txt
Documentation/config.txt
Documentation/config/init.txt
Documentation/config/lsrefs.txt [new file with mode: 0644]
Documentation/config/maintenance.txt
Documentation/config/mergetool.txt
Documentation/config/pack.txt
Documentation/git-branch.txt
Documentation/git-gc.txt
Documentation/git-index-pack.txt
Documentation/git-maintenance.txt
Documentation/git-mergetool--lib.txt
Documentation/git-range-diff.txt
Documentation/git-rev-list.txt
Documentation/git-shortlog.txt
Documentation/git-stash.txt
Documentation/rev-list-options.txt
Documentation/technical/commit-graph-format.txt
Documentation/technical/commit-graph.txt
Documentation/technical/hash-function-transition.txt
Documentation/technical/pack-format.txt
Documentation/technical/protocol-v2.txt
builtin/bisect--helper.c
builtin/blame.c
builtin/checkout-index.c
builtin/clone.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/gc.c
builtin/grep.c
builtin/index-pack.c
builtin/log.c
builtin/ls-remote.c
builtin/pack-objects.c
builtin/range-diff.c
builtin/receive-pack.c
builtin/reflog.c
builtin/repack.c
builtin/rev-list.c
builtin/stash.c
builtin/submodule--helper.c
builtin/tag.c
ci/run-build-and-tests.sh
commit-graph.c
commit-graph.h
commit-reach.c
commit-reach.h
commit.c
commit.h
compat/precompose_utf8.c
compat/precompose_utf8.h
config.c
connect.c
contrib/completion/git-completion.bash
diff.c
diff.h
diffcore-rename.c
entry.c
fmt-merge-msg.c
fsck.c
git-bisect.sh
git-compat-util.h
git-difftool--helper.sh
git-mergetool--lib.sh
git-mergetool.sh
git.c
gpg-interface.c
gpg-interface.h
log-tree.c
ls-refs.c
ls-refs.h
mailmap.c
merge-ort.c
object-store.h
pack-bitmap.c
pack-bitmap.h
pack-revindex.c
pack-revindex.h
pack-write.c
pack.h
packfile.c
packfile.h
pager.c
parse-options.c
range-diff.c
range-diff.h
ref-filter.c
remote.h
revision.c
run-command.c
serve.c
t/.gitattributes
t/README
t/helper/test-read-graph.c
t/helper/test-trace2.c
t/lib-bitmap.sh [new file with mode: 0644]
t/lib-bundle.sh [moved from t/test-bundle-functions.sh with 100% similarity]
t/lib-credential.sh
t/lib-diff.sh [moved from t/diff-lib.sh with 100% similarity]
t/lib-diff/COPYING [moved from t/diff-lib/COPYING with 100% similarity]
t/lib-diff/README [moved from t/diff-lib/README with 100% similarity]
t/lib-gitweb.sh [moved from t/gitweb-lib.sh with 100% similarity]
t/lib-log-graph.sh
t/t0000-basic.sh
t/t0006-date.sh
t/t0008-ignores.sh
t/t0012-help.sh
t/t0018-advice.sh
t/t0020-crlf.sh
t/t0021-conversion.sh
t/t0027-auto-crlf.sh
t/t0040-parse-options.sh
t/t0090-cache-tree.sh
t/t0201-gettext-fallbacks.sh
t/t0210-trace2-normal.sh
t/t0300-credentials.sh
t/t0500-progress-display.sh
t/t1011-read-tree-sparse-checkout.sh
t/t1300-config.sh
t/t1308-config-set.sh
t/t1400-update-ref.sh
t/t1404-update-ref-errors.sh
t/t1410-reflog.sh
t/t1430-bad-ref-name.sh
t/t1450-fsck.sh
t/t1500-rev-parse.sh
t/t1502-rev-parse-parseopt.sh
t/t1506-rev-parse-diagnosis.sh
t/t1507-rev-parse-upstream.sh
t/t1509-root-work-tree.sh
t/t1512-rev-parse-disambiguation.sh
t/t1600-index.sh
t/t2006-checkout-index-basic.sh
t/t2018-checkout-branch.sh
t/t2020-checkout-detach.sh
t/t2104-update-index-skip-worktree.sh
t/t2200-add-update.sh
t/t2401-worktree-prune.sh
t/t2402-worktree-list.sh
t/t3005-ls-files-relative.sh
t/t3200-branch.sh
t/t3201-branch-contains.sh
t/t3203-branch-output.sh
t/t3206-range-diff.sh
t/t3300-funny-names.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3420-rebase-autostash.sh
t/t3504-cherry-pick-rerere.sh
t/t3507-cherry-pick-conflict.sh
t/t3508-cherry-pick-many-commits.sh
t/t3510-cherry-pick-sequence.sh
t/t3600-rm.sh
t/t3700-add.sh
t/t3701-add-interactive.sh
t/t3800-mktag.sh
t/t3903-stash.sh
t/t3905-stash-include-untracked.sh
t/t3910-mac-os-precompose.sh
t/t4000-diff-format.sh
t/t4001-diff-rename.sh
t/t4003-diff-rename-1.sh
t/t4004-diff-rename-symlink.sh
t/t4005-diff-rename-2.sh
t/t4006-diff-mode.sh
t/t4007-rename-3.sh
t/t4008-diff-break-rewrite.sh
t/t4009-diff-rename-4.sh
t/t4010-diff-pathspec.sh
t/t4011-diff-symlink.sh
t/t4012-diff-binary.sh
t/t4013-diff-various.sh
t/t4014-format-patch.sh
t/t4015-diff-whitespace.sh
t/t4016-diff-quote.sh
t/t4027-diff-submodule.sh
t/t4030-diff-textconv.sh
t/t4034-diff-words.sh
t/t4038-diff-combined.sh
t/t4045-diff-relative.sh
t/t4049-diff-stat-count.sh
t/t4061-diff-indent.sh
t/t4100-apply-stat.sh
t/t4150-am.sh
t/t4153-am-resume-override-opts.sh
t/t4203-mailmap.sh
t/t4205-log-pretty-formats.sh
t/t4206-log-follow-harder-copies.sh
t/t4208-log-magic-pathspec.sh
t/t4216-log-bloom.sh
t/t4254-am-corrupt.sh
t/t5000-tar-tree.sh
t/t5004-archive-corner-cases.sh
t/t5150-request-pull.sh
t/t5300-pack-object.sh
t/t5310-pack-bitmaps.sh
t/t5316-pack-delta-depth.sh
t/t5318-commit-graph.sh
t/t5319-multi-pack-index.sh
t/t5324-split-commit-graph.sh
t/t5325-reverse-index.sh [new file with mode: 0755]
t/t5411/common-functions.sh
t/t5411/once-0010-report-status-v1.sh
t/t5411/test-0000-standard-git-push.sh
t/t5411/test-0001-standard-git-push--porcelain.sh
t/t5411/test-0002-pre-receive-declined.sh
t/t5411/test-0003-pre-receive-declined--porcelain.sh
t/t5411/test-0011-no-hook-error.sh
t/t5411/test-0012-no-hook-error--porcelain.sh
t/t5411/test-0013-bad-protocol.sh
t/t5411/test-0014-bad-protocol--porcelain.sh
t/t5411/test-0020-report-ng.sh
t/t5411/test-0021-report-ng--porcelain.sh
t/t5411/test-0022-report-unexpect-ref.sh
t/t5411/test-0023-report-unexpect-ref--porcelain.sh
t/t5411/test-0024-report-unknown-ref.sh
t/t5411/test-0025-report-unknown-ref--porcelain.sh
t/t5411/test-0026-push-options.sh
t/t5411/test-0027-push-options--porcelain.sh
t/t5411/test-0030-report-ok.sh
t/t5411/test-0031-report-ok--porcelain.sh
t/t5411/test-0032-report-with-options.sh
t/t5411/test-0033-report-with-options--porcelain.sh
t/t5411/test-0034-report-ft.sh
t/t5411/test-0035-report-ft--porcelain.sh
t/t5411/test-0036-report-multi-rewrite-for-one-ref.sh
t/t5411/test-0037-report-multi-rewrite-for-one-ref--porcelain.sh
t/t5411/test-0038-report-mixed-refs.sh
t/t5411/test-0039-report-mixed-refs--porcelain.sh
t/t5411/test-0040-process-all-refs.sh
t/t5411/test-0041-process-all-refs--porcelain.sh
t/t5411/test-0050-proc-receive-refs-with-modifiers.sh
t/t5500-fetch-pack.sh
t/t5505-remote.sh
t/t5510-fetch.sh
t/t5512-ls-remote.sh
t/t5526-fetch-submodules.sh
t/t5541-http-push-smart.sh
t/t5544-pack-objects-hook.sh
t/t5601-clone.sh
t/t5604-clone-reference.sh
t/t5606-clone-options.sh
t/t5701-git-serve.sh
t/t5702-protocol-v2.sh
t/t5703-upload-pack-ref-in-want.sh
t/t6020-bundle-misc.sh
t/t6030-bisect-porcelain.sh
t/t6040-tracking-info.sh
t/t6113-rev-list-bitmap-filters.sh
t/t6115-rev-list-du.sh [new file with mode: 0755]
t/t6120-describe.sh
t/t6134-pathspec-in-submodule.sh
t/t6301-for-each-ref-errors.sh
t/t6404-recursive-merge.sh
t/t6423-merge-rename-directories.sh
t/t6426-merge-skip-unneeded-updates.sh
t/t6436-merge-overwrite.sh
t/t6439-merge-co-error-msgs.sh
t/t6500-gc.sh
t/t6600-test-reach.sh
t/t7001-mv.sh
t/t7004-tag.sh
t/t7006-pager.sh
t/t7012-skip-worktree-writing.sh
t/t7060-wtstatus.sh
t/t7063-status-untracked-cache.sh
t/t7102-reset.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7401-submodule-summary.sh
t/t7406-submodule-update.sh
t/t7407-submodule-foreach.sh
t/t7500-commit-template-squash-signoff.sh
t/t7501-commit-basic-functionality.sh
t/t7502-commit-porcelain.sh
t/t7505-prepare-commit-msg-hook.sh
t/t7508-status.sh
t/t7510-signed-commit.sh
t/t7512-status-help.sh
t/t7519-status-fsmonitor.sh
t/t7521-ignored-mode.sh
t/t7600-merge.sh
t/t7602-merge-octopus-many.sh
t/t7610-mergetool.sh
t/t7810-grep.sh
t/t7900-maintenance.sh
t/t9003-help-autocorrect.sh
t/t9151-svn-mergeinfo.sh
t/t9300-fast-import.sh
t/t9500-gitweb-standalone-no-errors.sh
t/t9501-gitweb-standalone-http-status.sh
t/t9502-gitweb-standalone-parse-output.sh
t/test-lib-functions.sh
t/test-lib.sh
tag.c
tmp-objdir.c
transport-helper.c
transport-internal.h
transport.c
transport.h
upload-pack.c
usage.c

index 39efc411c79d590f13325d88fec9eb96529857c5..1d2dba2c80f330c14c835fa3ba5a1c924208105b 100644 (file)
@@ -60,6 +60,35 @@ UI, Workflows & Features
    locked and prunable attributes in --porcelain mode, and gained
    a --verbose option.
 
+ * "git clone" tries to locally check out the branch pointed at by
+   HEAD of the remote repository after it is done, but the protocol
+   did not convey the information necessary to do so when copying an
+   empty repository.  The protocol v2 learned how to do so.
+
+ * There are other ways than ".." for a single token to denote a
+   "commit range", namely "<rev>^!" and "<rev>^-<n>", but "git
+   range-diff" did not understand them.
+
+ * The "git range-diff" command learned "--(left|right)-only" option
+   to show only one side of the compared range.
+
+ * "git mergetool" feeds three versions (base, local and remote) of
+   a conflicted path unmodified.  The command learned to optionally
+   prepare these files with unconflicted parts already resolved.
+
+ * The .mailmap is documented to be read only from the root level of a
+   working tree, but a stray file in a bare repository also was read
+   by accident, which has been corrected.
+
+ * "git maintenance" tool learned a new "pack-refs" maintenance task.
+
+ * The error message given when a configuration variable that is
+   expected to have a boolean value has been improved.
+
+ * Signed commits and tags now allow verification of objects, whose
+   two object names (one in SHA-1, the other in SHA-256) are both
+   signed.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -136,6 +165,17 @@ Performance, Internal Implementation, Development Support etc.
  * Get rid of "GETTEXT_POISON" support altogether, which may or may
    not be controversial.
 
+ * Introduce an on-disk file to record revindex for packdata, which
+   traditionally was always created on the fly and only in-core.
+
+ * The commit-graph learned to use corrected commit dates instead of
+   the generation number to help topological revision traversal.
+
+ * Piecemeal of rewrite of "git bisect" in C continues.
+
+ * When a pager spawned by us exited, the trace log did not record its
+   exit status correctly, which has been corrected.
+
 
 Fixes since v2.30
 -----------------
@@ -188,5 +228,51 @@ Fixes since v2.30
    processing.  This has been corrected.
    (merge f7d42ceec5 js/rebase-i-commit-cleanup-fix later to maint).
 
+ * Fix in passing custom args from "git clone" to "upload-pack" on the
+   other side.
+   (merge ad6b5fefbd jv/upload-pack-filter-spec-quotefix later to maint).
+
+ * The command line completion (in contrib/) completed "git branch -d"
+   with branch names, but "git branch -D" offered tagnames in addition,
+   which has been corrected.  "git branch -M" had the same problem.
+   (merge 27dc071b9a jk/complete-branch-force-delete later to maint).
+
+ * When commands are started from a subdirectory, they may have to
+   compare the path to the subdirectory (called prefix and found out
+   from $(pwd)) with the tracked paths.  On macOS, $(pwd) and
+   readdir() yield decomposed path, while the tracked paths are
+   usually normalized to the precomposed form, causing mismatch.  This
+   has been fixed by taking the same approach used to normalize the
+   command line arguments.
+   (merge 5c327502db tb/precompose-prefix-too later to maint).
+
+ * Even though invocations of "die()" were logged to the trace2
+   system, "BUG()"s were not, which has been corrected.
+   (merge 0a9dde4a04 jt/trace2-BUG later to maint).
+
+ * "git grep --untracked" is meant to be "let's ALSO find in these
+   files on the filesystem" when looking for matches in the working
+   tree files, and does not make any sense if the primary search is
+   done against the index, or the tree objects.  The "--cached" and
+   "--untracked" options have been marked as mutually incompatible.
+   (merge 0c5d83b248 mt/grep-cached-untracked later to maint).
+
+ * Fix "git fsck --name-objects" which apparently has not been used by
+   anybody who is motivated enough to report breakage.
+   (merge e89f89361c js/fsck-name-objects-fix later to maint).
+
+ * Avoid individual tests in t5411 from getting affected by each other
+   by forcing them to use separate output files during the test.
+   (merge 822ee894f6 jx/t5411-unique-filenames later to maint).
+
+ * Test to make sure "git rev-parse one-thing one-thing" gives
+   the same thing twice (when one-thing is --since=X).
+   (merge a5cdca4520 ew/rev-parse-since-test later to maint).
+
+ * When certain features (e.g. grafts) used in the repository are
+   incompatible with the use of the commit-graph, we used to silently
+   turned commit-graph off; we now tell the user what we are doing.
+   (merge c85eec7fc3 js/commit-graph-warning later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge e3f5da7e60 sg/t7800-difftool-robustify later to maint).
index 6ba50b1104aa798cc80c92a521da39a026d82955..d08e83a1482ed4174d9003fba302b4dca5e7b9ed 100644 (file)
@@ -398,6 +398,8 @@ include::config/interactive.txt[]
 
 include::config/log.txt[]
 
+include::config/lsrefs.txt[]
+
 include::config/mailinfo.txt[]
 
 include::config/mailmap.txt[]
index dc77f8c8446801fddbce2dff1ef4e678f431e202..79c79d66174ebd9f2dfb867a6a39a339529b7fc0 100644 (file)
@@ -4,4 +4,4 @@ init.templateDir::
 
 init.defaultBranch::
        Allows overriding the default branch name e.g. when initializing
-       a new repository or when cloning an empty repository.
+       a new repository.
diff --git a/Documentation/config/lsrefs.txt b/Documentation/config/lsrefs.txt
new file mode 100644 (file)
index 0000000..adeda0f
--- /dev/null
@@ -0,0 +1,9 @@
+lsrefs.unborn::
+       May be "advertise" (the default), "allow", or "ignore". If "advertise",
+       the server will respond to the client sending "unborn" (as described in
+       protocol-v2.txt) and will advertise support for this feature during the
+       protocol v2 capability advertisement. "allow" is the same as
+       "advertise" except that the server will not advertise support for this
+       feature; this is useful for load-balanced servers that cannot be
+       updated atomically (for example), since the administrator could
+       configure "allow", then after a delay, configure "advertise".
index a5ead09e4bc2d1ca8834b332322315bcc72f352c..18f056213145e595d0a57792e716f29521122997 100644 (file)
@@ -15,8 +15,9 @@ maintenance.strategy::
 * `none`: This default setting implies no task are run at any schedule.
 * `incremental`: This setting optimizes for performing small maintenance
   activities that do not delete any data. This does not schedule the `gc`
-  task, but runs the `prefetch` and `commit-graph` tasks hourly and the
-  `loose-objects` and `incremental-repack` tasks daily.
+  task, but runs the `prefetch` and `commit-graph` tasks hourly, the
+  `loose-objects` and `incremental-repack` tasks daily, and the `pack-refs`
+  task weekly.
 
 maintenance.<task>.enabled::
        This boolean config option controls whether the maintenance task
index 16a27443a3edc1d1a1f25ea03bad04896caa59c8..90f76f5b9ba8ddd82ecc4ef3f89185f8e8298f23 100644 (file)
@@ -13,6 +13,11 @@ mergetool.<tool>.cmd::
        merged; 'MERGED' contains the name of the file to which the merge
        tool should write the results of a successful merge.
 
+mergetool.<tool>.hideResolved::
+       Allows the user to override the global `mergetool.hideResolved` value
+       for a specific tool. See `mergetool.hideResolved` for the full
+       description.
+
 mergetool.<tool>.trustExitCode::
        For a custom merge command, specify whether the exit code of
        the merge command can be used to determine whether the merge was
@@ -40,6 +45,16 @@ mergetool.meld.useAutoMerge::
        value of `false` avoids using `--auto-merge` altogether, and is the
        default value.
 
+mergetool.hideResolved::
+       During a merge Git will automatically resolve as many conflicts as
+       possible and write the 'MERGED' file containing conflict markers around
+       any conflicts that it cannot resolve; 'LOCAL' and 'REMOTE' normally
+       represent the versions of the file from before Git's conflict
+       resolution. This flag causes 'LOCAL' and 'REMOTE' to be overwriten so
+       that only the unresolved conflicts are presented to the merge tool. Can
+       be configured per-tool via the `mergetool.<tool>.hideResolved`
+       configuration variable. Defaults to `true`.
+
 mergetool.keepBackup::
        After performing a merge, the original file with conflict markers
        can be saved as a file with a `.orig` extension.  If this variable
index 837f1b16792fe2200afe011cc1dc0868b6590fe6..3da4ea98e2d4b11d0ff3534efd71ceddb93d9ea8 100644 (file)
@@ -133,3 +133,10 @@ pack.writeBitmapHashCache::
        between an older, bitmapped pack and objects that have been
        pushed since the last gc). The downside is that it consumes 4
        bytes per object of disk space. Defaults to true.
+
+pack.writeReverseIndex::
+       When true, git will write a corresponding .rev file (see:
+       link:../technical/pack-format.html[Documentation/technical/pack-format.txt])
+       for each new packfile that it writes in all places except for
+       linkgit:git-fast-import[1] and in the bulk checkin mechanism.
+       Defaults to false.
index adaa1782a8b26d4c5fb498ff3a1d33abb8e6ac19..eb815c22484ef6b67b09c787d9b0caabb0098028 100644 (file)
@@ -78,8 +78,8 @@ renaming. If <newbranch> exists, -M must be used to force the rename
 to happen.
 
 The `-c` and `-C` options have the exact same semantics as `-m` and
-`-M`, except instead of the branch being renamed it along with its
-config and reflog will be copied to a new name.
+`-M`, except instead of the branch being renamed, it will be copied to a
+new name, along with its config and reflog.
 
 With a `-d` or `-D` option, `<branchname>` will be deleted.  You may
 specify more than one branch for deletion.  If the branch currently
index 0c114ad1ca26181776032c3cde957230f23aa602..853967dea01d0778e8d1d484afa9098ab4532ffc 100644 (file)
@@ -117,12 +117,14 @@ NOTES
 'git gc' tries very hard not to delete objects that are referenced
 anywhere in your repository. In particular, it will keep not only
 objects referenced by your current set of branches and tags, but also
-objects referenced by the index, remote-tracking branches, notes saved
-by 'git notes' under refs/notes/, reflogs (which may reference commits
-in branches that were later amended or rewound), and anything else in
-the refs/* namespace.  If you are expecting some objects to be deleted
-and they aren't, check all of those locations and decide whether it
-makes sense in your case to remove those references.
+objects referenced by the index, remote-tracking branches, reflogs
+(which may reference commits in branches that were later amended or
+rewound), and anything else in the refs/* namespace. Note that a note
+(of the kind created by 'git notes') attached to an object does not
+contribute in keeping the object alive. If you are expecting some
+objects to be deleted and they aren't, check all of those locations
+and decide whether it makes sense in your case to remove those
+references.
 
 On the other hand, when 'git gc' runs concurrently with another process,
 there is a risk of it deleting an object that the other process is using
index af0c26232c1e775cf5f4d56ee67c07b6a73a9104..69ba904d4491957dcf739097e739357aa5969891 100644 (file)
@@ -9,17 +9,18 @@ git-index-pack - Build pack index file for an existing packed archive
 SYNOPSIS
 --------
 [verse]
-'git index-pack' [-v] [-o <index-file>] <pack-file>
+'git index-pack' [-v] [-o <index-file>] [--[no-]rev-index] <pack-file>
 'git index-pack' --stdin [--fix-thin] [--keep] [-v] [-o <index-file>]
-                 [<pack-file>]
+                 [--[no-]rev-index] [<pack-file>]
 
 
 DESCRIPTION
 -----------
 Reads a packed archive (.pack) from the specified file, and
-builds a pack index file (.idx) for it.  The packed archive
-together with the pack index can then be placed in the
-objects/pack/ directory of a Git repository.
+builds a pack index file (.idx) for it. Optionally writes a
+reverse-index (.rev) for the specified pack. The packed
+archive together with the pack index can then be placed in
+the objects/pack/ directory of a Git repository.
 
 
 OPTIONS
@@ -35,6 +36,13 @@ OPTIONS
        fails if the name of packed archive does not end
        with .pack).
 
+--[no-]rev-index::
+       When this flag is provided, generate a reverse index
+       (a `.rev` file) corresponding to the given pack. If
+       `--verify` is given, ensure that the existing
+       reverse index is correct. Takes precedence over
+       `pack.writeReverseIndex`.
+
 --stdin::
        When this flag is provided, the pack is read from stdin
        instead and a copy is then written to <pack-file>. If
index 3b432171d60826f50ad1251d7fa07f3d8f15cc1a..80ddd33ceba0dd5f47319ea8355fec7d35a74bef 100644 (file)
@@ -145,6 +145,12 @@ incremental-repack::
        which is a special case that attempts to repack all pack-files
        into a single pack-file.
 
+pack-refs::
+       The `pack-refs` task collects the loose reference files and
+       collects them into a single file. This speeds up operations that
+       need to iterate across many references. See linkgit:git-pack-refs[1]
+       for more information.
+
 OPTIONS
 -------
 --auto::
index 4da9d240962f24da7daa7ebde7b4f0db4634cdd8..3e8f59ac0e46abc35b88723492fa2d411b2756ca 100644 (file)
@@ -38,6 +38,10 @@ get_merge_tool_cmd::
 get_merge_tool_path::
        returns the custom path for a merge tool.
 
+initialize_merge_tool::
+       bring merge tool specific functions into scope so they can be used or
+       overridden.
+
 run_merge_tool::
        launches a merge tool given the tool name and a true/false
        flag to indicate whether a merge base is present.
index 9701c1e5fdd5a016c360b8102f0193dfa0fb90fb..fe350d7f4056e280c9740ae9c601f0c7eb2a1119 100644 (file)
@@ -10,6 +10,7 @@ SYNOPSIS
 [verse]
 'git range-diff' [--color=[<when>]] [--no-color] [<diff-options>]
        [--no-dual-color] [--creation-factor=<factor>]
+       [--left-only | --right-only]
        ( <range1> <range2> | <rev1>...<rev2> | <base> <rev1> <rev2> )
 
 DESCRIPTION
@@ -28,6 +29,17 @@ Finally, the list of matching commits is shown in the order of the
 second commit range, with unmatched commits being inserted just after
 all of their ancestors have been shown.
 
+There are three ways to specify the commit ranges:
+
+- `<range1> <range2>`: Either commit range can be of the form
+  `<base>..<rev>`, `<rev>^!` or `<rev>^-<n>`. See `SPECIFYING RANGES`
+  in linkgit:gitrevisions[7] for more details.
+
+- `<rev1>...<rev2>`. This is equivalent to
+  `<rev2>..<rev1> <rev1>..<rev2>`.
+
+- `<base> <rev1> <rev2>`: This is equivalent to `<base>..<rev1>
+  <base>..<rev2>`.
 
 OPTIONS
 -------
@@ -57,6 +69,14 @@ to revert to color all lines according to the outer diff markers
        See the ``Algorithm`` section below for an explanation why this is
        needed.
 
+--left-only::
+       Suppress commits that are missing from the first specified range
+       (or the "left range" when using the `<rev1>...<rev2>` format).
+
+--right-only::
+       Suppress commits that are missing from the second specified range
+       (or the "right range" when using the `<rev1>...<rev2>` format).
+
 --[no-]notes[=<ref>]::
        This flag is passed to the `git log` program
        (see linkgit:git-log[1]) that generates the patches.
index 5da66232dc3f7b21106cdeba55cc1d94bce04393..20bb8e82176b89cbbe68403301a43848b2bef912 100644 (file)
@@ -31,6 +31,99 @@ include::rev-list-options.txt[]
 
 include::pretty-formats.txt[]
 
+EXAMPLES
+--------
+
+* Print the list of commits reachable from the current branch.
++
+----------
+git rev-list HEAD
+----------
+
+* Print the list of commits on this branch, but not present in the
+  upstream branch.
++
+----------
+git rev-list @{upstream}..HEAD
+----------
+
+* Format commits with their author and commit message (see also the
+  porcelain linkgit:git-log[1]).
++
+----------
+git rev-list --format=medium HEAD
+----------
+
+* Format commits along with their diffs (see also the porcelain
+  linkgit:git-log[1], which can do this in a single process).
++
+----------
+git rev-list HEAD |
+git diff-tree --stdin --format=medium -p
+----------
+
+* Print the list of commits on the current branch that touched any
+  file in the `Documentation` directory.
++
+----------
+git rev-list HEAD -- Documentation/
+----------
+
+* Print the list of commits authored by you in the past year, on
+  any branch, tag, or other ref.
++
+----------
+git rev-list --author=you@example.com --since=1.year.ago --all
+----------
+
+* Print the list of objects reachable from the current branch (i.e., all
+  commits and the blobs and trees they contain).
++
+----------
+git rev-list --objects HEAD
+----------
+
+* Compare the disk size of all reachable objects, versus those
+  reachable from reflogs, versus the total packed size. This can tell
+  you whether running `git repack -ad` might reduce the repository size
+  (by dropping unreachable objects), and whether expiring reflogs might
+  help.
++
+----------
+# reachable objects
+git rev-list --disk-usage --objects --all
+# plus reflogs
+git rev-list --disk-usage --objects --all --reflog
+# total disk size used
+du -c .git/objects/pack/*.pack .git/objects/??/*
+# alternative to du: add up "size" and "size-pack" fields
+git count-objects -v
+----------
+
+* Report the disk size of each branch, not including objects used by the
+  current branch. This can find outliers that are contributing to a
+  bloated repository size (e.g., because somebody accidentally committed
+  large build artifacts).
++
+----------
+git for-each-ref --format='%(refname)' |
+while read branch
+do
+       size=$(git rev-list --disk-usage --objects HEAD..$branch)
+       echo "$size $branch"
+done |
+sort -n
+----------
+
+* Compare the on-disk size of branches in one group of refs, excluding
+  another. If you co-mingle objects from multiple remotes in a single
+  repository, this can show which remotes are contributing to the
+  repository size (taking the size of `origin` as a baseline).
++
+----------
+git rev-list --disk-usage --objects --remotes=$suspect --not --remotes=origin
+----------
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index c16cc3b6089c6333de23117e5acaff36fa30b76f..c9c7f3065cafcc58efd795e58fb0d0a73da5b77a 100644 (file)
@@ -113,6 +113,10 @@ MAPPING AUTHORS
 
 See linkgit:gitmailmap[5].
 
+Note that if `git shortlog` is run outside of a repository (to process
+log contents on standard input), it will look for a `.mailmap` file in
+the current directory.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
index 31f1beb65baf213908e93d0e90c379d9fb0e7d84..f1197d641ba631fb40505febcb6de5e79c281e5a 100644 (file)
@@ -8,8 +8,8 @@ git-stash - Stash the changes in a dirty working directory away
 SYNOPSIS
 --------
 [verse]
-'git stash' list [<options>]
-'git stash' show [<options>] [<stash>]
+'git stash' list [<log-options>]
+'git stash' show [<diff-options>] [<stash>]
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
@@ -67,7 +67,7 @@ save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q
        Instead, all non-option arguments are concatenated to form the stash
        message.
 
-list [<options>]::
+list [<log-options>]::
 
        List the stash entries that you currently have.  Each 'stash entry' is
        listed with its name (e.g. `stash@{0}` is the latest entry, `stash@{1}` is
@@ -83,7 +83,7 @@ stash@{1}: On master: 9cc0589... Add git-stash
 The command takes options applicable to the 'git log'
 command to control what is shown and how. See linkgit:git-log[1].
 
-show [<options>] [<stash>]::
+show [<diff-options>] [<stash>]::
 
        Show the changes recorded in the stash entry as a diff between the
        stashed contents and the commit back when the stash entry was first
index 96cc89d157d21f0cbf9cee35db0c49bc4cb18dae..1238bfd915d23144ad60260609780f138f80785c 100644 (file)
@@ -227,6 +227,15 @@ ifdef::git-rev-list[]
        test the exit status to see if a range of objects is fully
        connected (or not).  It is faster than redirecting stdout
        to `/dev/null` as the output does not have to be formatted.
+
+--disk-usage::
+       Suppress normal output; instead, print the sum of the bytes used
+       for on-disk storage by the selected commits or objects. This is
+       equivalent to piping the output into `git cat-file
+       --batch-check='%(objectsize:disk)'`, except that it runs much
+       faster (especially with `--use-bitmap-index`). See the `CAVEATS`
+       section in linkgit:git-cat-file[1] for the limitations of what
+       "on-disk storage" means.
 endif::git-rev-list[]
 
 --cherry-mark::
index b3b58880b926a14036bd2e05c4823ba19b9e7b37..b6658eff188209a0fbd4f5de2df525c61835a2b7 100644 (file)
@@ -4,11 +4,7 @@ Git commit graph format
 The Git commit graph stores a list of commit OIDs and some associated
 metadata, including:
 
-- The generation number of the commit. Commits with no parents have
-  generation number 1; commits with parents have generation number
-  one more than the maximum generation number of its parents. We
-  reserve zero as special, and can be used to mark a generation
-  number invalid or as "not computed".
+- The generation number of the commit.
 
 - The root tree OID.
 
@@ -86,13 +82,33 @@ CHUNK DATA:
       position. If there are more than two parents, the second value
       has its most-significant bit on and the other bits store an array
       position into the Extra Edge List chunk.
-    * The next 8 bytes store the generation number of the commit and
+    * The next 8 bytes store the topological level (generation number v1)
+      of the commit and
       the commit time in seconds since EPOCH. The generation number
       uses the higher 30 bits of the first 4 bytes, while the commit
       time uses the 32 bits of the second 4 bytes, along with the lowest
       2 bits of the lowest byte, storing the 33rd and 34th bit of the
       commit time.
 
+  Generation Data (ID: {'G', 'D', 'A', 'T' }) (N * 4 bytes) [Optional]
+    * This list of 4-byte values store corrected commit date offsets for the
+      commits, arranged in the same order as commit data chunk.
+    * If the corrected commit date offset cannot be stored within 31 bits,
+      the value has its most-significant bit on and the other bits store
+      the position of corrected commit date into the Generation Data Overflow
+      chunk.
+    * Generation Data chunk is present only when commit-graph file is written
+      by compatible versions of Git and in case of split commit-graph chains,
+      the topmost layer also has Generation Data chunk.
+
+  Generation Data Overflow (ID: {'G', 'D', 'O', 'V' }) [Optional]
+    * This list of 8-byte values stores the corrected commit date offsets
+      for commits with corrected commit date offsets that cannot be
+      stored within 31 bits.
+    * Generation Data Overflow chunk is present only when Generation Data
+      chunk is present and atleast one corrected commit date offset cannot
+      be stored within 31 bits.
+
   Extra Edge List (ID: {'E', 'D', 'G', 'E'}) [Optional]
       This list of 4-byte values store the second through nth parents for
       all octopus merges. The second parent value in the commit data stores
index f14a7659aa8733a2a992eb949caf1d4e7a67accf..f05e7bda1a9d66c6c19eec5861384cca86b6690d 100644 (file)
@@ -38,14 +38,31 @@ A consumer may load the following info for a commit from the graph:
 
 Values 1-4 satisfy the requirements of parse_commit_gently().
 
-Define the "generation number" of a commit recursively as follows:
+There are two definitions of generation number:
+1. Corrected committer dates (generation number v2)
+2. Topological levels (generation nummber v1)
 
- * A commit with no parents (a root commit) has generation number one.
+Define "corrected committer date" of a commit recursively as follows:
 
- * A commit with at least one parent has generation number one more than
-   the largest generation number among its parents.
+ * A commit with no parents (a root commit) has corrected committer date
+    equal to its committer date.
 
-Equivalently, the generation number of a commit A is one more than the
+ * A commit with at least one parent has corrected committer date equal to
+    the maximum of its commiter date and one more than the largest corrected
+    committer date among its parents.
+
+ * As a special case, a root commit with timestamp zero has corrected commit
+    date of 1, to be able to distinguish it from GENERATION_NUMBER_ZERO
+    (that is, an uncomputed corrected commit date).
+
+Define the "topological level" of a commit recursively as follows:
+
+ * A commit with no parents (a root commit) has topological level of one.
+
+ * A commit with at least one parent has topological level one more than
+   the largest topological level among its parents.
+
+Equivalently, the topological level of a commit A is one more than the
 length of a longest path from A to a root commit. The recursive definition
 is easier to use for computation and observing the following property:
 
@@ -60,6 +77,9 @@ is easier to use for computation and observing the following property:
     generation numbers, then we always expand the boundary commit with highest
     generation number and can easily detect the stopping condition.
 
+The property applies to both versions of generation number, that is both
+corrected committer dates and topological levels.
+
 This property can be used to significantly reduce the time it takes to
 walk commits and determine topological relationships. Without generation
 numbers, the general heuristic is the following:
@@ -67,7 +87,9 @@ numbers, the general heuristic is the following:
     If A and B are commits with commit time X and Y, respectively, and
     X < Y, then A _probably_ cannot reach B.
 
-This heuristic is currently used whenever the computation is allowed to
+In absence of corrected commit dates (for example, old versions of Git or
+mixed generation graph chains),
+this heuristic is currently used whenever the computation is allowed to
 violate topological relationships due to clock skew (such as "git log"
 with default order), but is not used when the topological order is
 required (such as merge base calculations, "git log --graph").
@@ -77,7 +99,7 @@ in the commit graph. We can treat these commits as having "infinite"
 generation number and walk until reaching commits with known generation
 number.
 
-We use the macro GENERATION_NUMBER_INFINITY = 0xFFFFFFFF to mark commits not
+We use the macro GENERATION_NUMBER_INFINITY to mark commits not
 in the commit-graph file. If a commit-graph file was written by a version
 of Git that did not compute generation numbers, then those commits will
 have generation number represented by the macro GENERATION_NUMBER_ZERO = 0.
@@ -93,12 +115,12 @@ fully-computed generation numbers. Using strict inequality may result in
 walking a few extra commits, but the simplicity in dealing with commits
 with generation number *_INFINITY or *_ZERO is valuable.
 
-We use the macro GENERATION_NUMBER_MAX = 0x3FFFFFFF to for commits whose
-generation numbers are computed to be at least this value. We limit at
-this value since it is the largest value that can be stored in the
-commit-graph file using the 30 bits available to generation numbers. This
-presents another case where a commit can have generation number equal to
-that of a parent.
+We use the macro GENERATION_NUMBER_V1_MAX = 0x3FFFFFFF for commits whose
+topological levels (generation number v1) are computed to be at least
+this value. We limit at this value since it is the largest value that
+can be stored in the commit-graph file using the 30 bits available
+to topological levels. This presents another case where a commit can
+have generation number equal to that of a parent.
 
 Design Details
 --------------
@@ -267,6 +289,35 @@ The merge strategy values (2 for the size multiple, 64,000 for the maximum
 number of commits) could be extracted into config settings for full
 flexibility.
 
+## Handling Mixed Generation Number Chains
+
+With the introduction of generation number v2 and generation data chunk, the
+following scenario is possible:
+
+1. "New" Git writes a commit-graph with the corrected commit dates.
+2. "Old" Git writes a split commit-graph on top without corrected commit dates.
+
+A naive approach of using the newest available generation number from
+each layer would lead to violated expectations: the lower layer would
+use corrected commit dates which are much larger than the topological
+levels of the higher layer. For this reason, Git inspects the topmost
+layer to see if the layer is missing corrected commit dates. In such a case
+Git only uses topological level for generation numbers.
+
+When writing a new layer in split commit-graph, we write corrected commit
+dates if the topmost layer has corrected commit dates written. This
+guarantees that if a layer has corrected commit dates, all lower layers
+must have corrected commit dates as well.
+
+When merging layers, we do not consider whether the merged layers had corrected
+commit dates. Instead, the new layer will have corrected commit dates if the
+layer below the new layer has corrected commit dates.
+
+While writing or merging layers, if the new layer is the only layer, it will
+have corrected commit dates when written by compatible versions of Git. Thus,
+rewriting split commit-graph as a single file (`--split=replace`) creates a
+single layer with corrected commit dates.
+
 ## Deleting graph-{hash} files
 
 After a new tip file is written, some `graph-{hash}` files may no longer
index 6fd20ebbc254472936238a8db590585e93d5eb91..7c1630bf832491080b043e5c2e3126272dc0408c 100644 (file)
@@ -33,16 +33,9 @@ researchers. On 23 February 2017 the SHAttered attack
 
 Git v2.13.0 and later subsequently moved to a hardened SHA-1
 implementation by default, which isn't vulnerable to the SHAttered
-attack.
+attack, but SHA-1 is still weak.
 
-Thus Git has in effect already migrated to a new hash that isn't SHA-1
-and doesn't share its vulnerabilities, its new hash function just
-happens to produce exactly the same output for all known inputs,
-except two PDFs published by the SHAttered researchers, and the new
-implementation (written by those researchers) claims to detect future
-cryptanalytic collision attacks.
-
-Regardless, it's considered prudent to move past any variant of SHA-1
+Thus it's considered prudent to move past any variant of SHA-1
 to a new hash. There's no guarantee that future attacks on SHA-1 won't
 be published in the future, and those attacks may not have viable
 mitigations.
@@ -57,6 +50,38 @@ SHA-1 still possesses the other properties such as fast object lookup
 and safe error checking, but other hash functions are equally suitable
 that are believed to be cryptographically secure.
 
+Choice of Hash
+--------------
+The hash to replace the hardened SHA-1 should be stronger than SHA-1
+was: we would like it to be trustworthy and useful in practice for at
+least 10 years.
+
+Some other relevant properties:
+
+1. A 256-bit hash (long enough to match common security practice; not
+   excessively long to hurt performance and disk usage).
+
+2. High quality implementations should be widely available (e.g., in
+   OpenSSL and Apple CommonCrypto).
+
+3. The hash function's properties should match Git's needs (e.g. Git
+   requires collision and 2nd preimage resistance and does not require
+   length extension resistance).
+
+4. As a tiebreaker, the hash should be fast to compute (fortunately
+   many contenders are faster than SHA-1).
+
+There were several contenders for a successor hash to SHA-1, including
+SHA-256, SHA-512/256, SHA-256x16, K12, and BLAKE2bp-256.
+
+In late 2018 the project picked SHA-256 as its successor hash.
+
+See 0ed8d8da374 (doc hash-function-transition: pick SHA-256 as
+NewHash, 2018-08-04) and numerous mailing list threads at the time,
+particularly the one starting at
+https://lore.kernel.org/git/20180609224913.GC38834@genre.crustytoothpaste.net/
+for more information.
+
 Goals
 -----
 1. The transition to SHA-256 can be done one local repository at a time.
@@ -94,7 +119,7 @@ Overview
 --------
 We introduce a new repository format extension. Repositories with this
 extension enabled use SHA-256 instead of SHA-1 to name their objects.
-This affects both object names and object content --- both the names
+This affects both object names and object content -- both the names
 of objects and all references to other objects within an object are
 switched to the new hash function.
 
@@ -107,7 +132,7 @@ mapping to allow naming objects using either their SHA-1 and SHA-256 names
 interchangeably.
 
 "git cat-file" and "git hash-object" gain options to display an object
-in its sha1 form and write an object given its sha1 form. This
+in its SHA-1 form and write an object given its SHA-1 form. This
 requires all objects referenced by that object to be present in the
 object database so that they can be named using the appropriate name
 (using the bidirectional hash mapping).
@@ -115,7 +140,7 @@ object database so that they can be named using the appropriate name
 Fetches from a SHA-1 based server convert the fetched objects into
 SHA-256 form and record the mapping in the bidirectional mapping table
 (see below for details). Pushes to a SHA-1 based server convert the
-objects being pushed into sha1 form so the server does not have to be
+objects being pushed into SHA-1 form so the server does not have to be
 aware of the hash function the client is using.
 
 Detailed Design
@@ -151,38 +176,38 @@ repository extensions.
 
 Object names
 ~~~~~~~~~~~~
-Objects can be named by their 40 hexadecimal digit sha1-name or 64
-hexadecimal digit sha256-name, plus names derived from those (see
+Objects can be named by their 40 hexadecimal digit SHA-1 name or 64
+hexadecimal digit SHA-256 name, plus names derived from those (see
 gitrevisions(7)).
 
-The sha1-name of an object is the SHA-1 of the concatenation of its
-type, length, a nul byte, and the object's sha1-content. This is the
+The SHA-1 name of an object is the SHA-1 of the concatenation of its
+type, length, a nul byte, and the object's SHA-1 content. This is the
 traditional <sha1> used in Git to name objects.
 
-The sha256-name of an object is the SHA-256 of the concatenation of its
-type, length, a nul byte, and the object's sha256-content.
+The SHA-256 name of an object is the SHA-256 of the concatenation of its
+type, length, a nul byte, and the object's SHA-256 content.
 
 Object format
 ~~~~~~~~~~~~~
 The content as a byte sequence of a tag, commit, or tree object named
-by sha1 and sha256 differ because an object named by sha256-name refers to
-other objects by their sha256-names and an object named by sha1-name
-refers to other objects by their sha1-names.
+by SHA-1 and SHA-256 differ because an object named by SHA-256 name refers to
+other objects by their SHA-256 names and an object named by SHA-1 name
+refers to other objects by their SHA-1 names.
 
-The sha256-content of an object is the same as its sha1-content, except
-that objects referenced by the object are named using their sha256-names
-instead of sha1-names. Because a blob object does not refer to any
-other object, its sha1-content and sha256-content are the same.
+The SHA-256 content of an object is the same as its SHA-1 content, except
+that objects referenced by the object are named using their SHA-256 names
+instead of SHA-1 names. Because a blob object does not refer to any
+other object, its SHA-1 content and SHA-256 content are the same.
 
-The format allows round-trip conversion between sha256-content and
-sha1-content.
+The format allows round-trip conversion between SHA-256 content and
+SHA-1 content.
 
 Object storage
 ~~~~~~~~~~~~~~
 Loose objects use zlib compression and packed objects use the packed
 format described in Documentation/technical/pack-format.txt, just like
-today. The content that is compressed and stored uses sha256-content
-instead of sha1-content.
+today. The content that is compressed and stored uses SHA-256 content
+instead of SHA-1 content.
 
 Pack index
 ~~~~~~~~~~
@@ -191,21 +216,21 @@ hash functions. They have the following format (all integers are in
 network byte order):
 
 - A header appears at the beginning and consists of the following:
-  - The 4-byte pack index signature: '\377t0c'
-  - 4-byte version number: 3
-  - 4-byte length of the header section, including the signature and
+  * The 4-byte pack index signature: '\377t0c'
+  * 4-byte version number: 3
+  * 4-byte length of the header section, including the signature and
     version number
-  - 4-byte number of objects contained in the pack
-  - 4-byte number of object formats in this pack index: 2
-  - For each object format:
-    - 4-byte format identifier (e.g., 'sha1' for SHA-1)
-    - 4-byte length in bytes of shortened object names. This is the
+  * 4-byte number of objects contained in the pack
+  * 4-byte number of object formats in this pack index: 2
+  * For each object format:
+    ** 4-byte format identifier (e.g., 'sha1' for SHA-1)
+    ** 4-byte length in bytes of shortened object names. This is the
       shortest possible length needed to make names in the shortened
       object name table unambiguous.
-    - 4-byte integer, recording where tables relating to this format
+    ** 4-byte integer, recording where tables relating to this format
       are stored in this index file, as an offset from the beginning.
-  - 4-byte offset to the trailer from the beginning of this file.
-  - Zero or more additional key/value pairs (4-byte key, 4-byte
+  * 4-byte offset to the trailer from the beginning of this file.
+  * Zero or more additional key/value pairs (4-byte key, 4-byte
     value). Only one key is supported: 'PSRC'. See the "Loose objects
     and unreachable objects" section for supported values and how this
     is used.  All other keys are reserved. Readers must ignore
@@ -213,37 +238,36 @@ network byte order):
 - Zero or more NUL bytes. This can optionally be used to improve the
   alignment of the full object name table below.
 - Tables for the first object format:
-  - A sorted table of shortened object names.  These are prefixes of
+  * A sorted table of shortened object names.  These are prefixes of
     the names of all objects in this pack file, packed together
     without offset values to reduce the cache footprint of the binary
     search for a specific object name.
 
-  - A table of full object names in pack order. This allows resolving
+  * A table of full object names in pack order. This allows resolving
     a reference to "the nth object in the pack file" (from a
     reachability bitmap or from the next table of another object
     format) to its object name.
 
-  - A table of 4-byte values mapping object name order to pack order.
+  * A table of 4-byte values mapping object name order to pack order.
     For an object in the table of sorted shortened object names, the
     value at the corresponding index in this table is the index in the
     previous table for that same object.
-
     This can be used to look up the object in reachability bitmaps or
     to look up its name in another object format.
 
-  - A table of 4-byte CRC32 values of the packed object data, in the
+  * A table of 4-byte CRC32 values of the packed object data, in the
     order that the objects appear in the pack file. This is to allow
     compressed data to be copied directly from pack to pack during
     repacking without undetected data corruption.
 
-  - A table of 4-byte offset values. For an object in the table of
+  * A table of 4-byte offset values. For an object in the table of
     sorted shortened object names, the value at the corresponding
     index in this table indicates where that object can be found in
     the pack file. These are usually 31-bit pack file offsets, but
     large offsets are encoded as an index into the next table with the
     most significant bit set.
 
-  - A table of 8-byte offset entries (empty for pack files less than
+  * A table of 8-byte offset entries (empty for pack files less than
     2 GiB). Pack files are organized with heavily used objects toward
     the front, so most object references should not need to refer to
     this table.
@@ -252,10 +276,10 @@ network byte order):
   up to and not including the table of CRC32 values.
 - Zero or more NUL bytes.
 - The trailer consists of the following:
-  - A copy of the 20-byte SHA-256 checksum at the end of the
+  * A copy of the 20-byte SHA-256 checksum at the end of the
     corresponding packfile.
 
-  - 20-byte SHA-256 checksum of all of the above.
+  * 20-byte SHA-256 checksum of all of the above.
 
 Loose object index
 ~~~~~~~~~~~~~~~~~~
@@ -288,18 +312,18 @@ To remove entries (e.g. in "git pack-refs" or "git-prune"):
 
 Translation table
 ~~~~~~~~~~~~~~~~~
-The index files support a bidirectional mapping between sha1-names
-and sha256-names. The lookup proceeds similarly to ordinary object
-lookups. For example, to convert a sha1-name to a sha256-name:
+The index files support a bidirectional mapping between SHA-1 names
+and SHA-256 names. The lookup proceeds similarly to ordinary object
+lookups. For example, to convert a SHA-1 name to a SHA-256 name:
 
  1. Look for the object in idx files. If a match is present in the
-    idx's sorted list of truncated sha1-names, then:
-    a. Read the corresponding entry in the sha1-name order to pack
+    idx's sorted list of truncated SHA-1 names, then:
+    a. Read the corresponding entry in the SHA-1 name order to pack
        name order mapping.
-    b. Read the corresponding entry in the full sha1-name table to
+    b. Read the corresponding entry in the full SHA-1 name table to
        verify we found the right object. If it is, then
-    c. Read the corresponding entry in the full sha256-name table.
-       That is the object's sha256-name.
+    c. Read the corresponding entry in the full SHA-256 name table.
+       That is the object's SHA-256 name.
  2. Check for a loose object. Read lines from loose-object-idx until
     we find a match.
 
@@ -313,10 +337,10 @@ Since all operations that make new objects (e.g., "git commit") add
 the new objects to the corresponding index, this mapping is possible
 for all objects in the object store.
 
-Reading an object's sha1-content
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The sha1-content of an object can be read by converting all sha256-names
-its sha256-content references to sha1-names using the translation table.
+Reading an object's SHA-1 content
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+The SHA-1 content of an object can be read by converting all SHA-256 names
+of its SHA-256 content references to SHA-1 names using the translation table.
 
 Fetch
 ~~~~~
@@ -339,7 +363,7 @@ the following steps:
 1. index-pack: inflate each object in the packfile and compute its
    SHA-1. Objects can contain deltas in OBJ_REF_DELTA format against
    objects the client has locally. These objects can be looked up
-   using the translation table and their sha1-content read as
+   using the translation table and their SHA-1 content read as
    described above to resolve the deltas.
 2. topological sort: starting at the "want"s from the negotiation
    phase, walk through objects in the pack and emit a list of them,
@@ -348,12 +372,12 @@ the following steps:
    (This list only contains objects reachable from the "wants". If the
    pack from the server contained additional extraneous objects, then
    they will be discarded.)
-3. convert to sha256: open a new (sha256) packfile. Read the topologically
+3. convert to SHA-256: open a new SHA-256 packfile. Read the topologically
    sorted list just generated. For each object, inflate its
-   sha1-content, convert to sha256-content, and write it to the sha256
-   pack. Record the new sha1<->sha256 mapping entry for use in the idx.
+   SHA-1 content, convert to SHA-256 content, and write it to the SHA-256
+   pack. Record the new SHA-1<-->SHA-256 mapping entry for use in the idx.
 4. sort: reorder entries in the new pack to match the order of objects
-   in the pack the server generated and include blobs. Write a sha256 idx
+   in the pack the server generated and include blobs. Write a SHA-256 idx
    file
 5. clean up: remove the SHA-1 based pack file, index, and
    topologically sorted list obtained from the server in steps 1
@@ -378,19 +402,20 @@ experimenting to get this to perform well.
 Push
 ~~~~
 Push is simpler than fetch because the objects referenced by the
-pushed objects are already in the translation table. The sha1-content
+pushed objects are already in the translation table. The SHA-1 content
 of each object being pushed can be read as described in the "Reading
-an object's sha1-content" section to generate the pack written by git
+an object's SHA-1 content" section to generate the pack written by git
 send-pack.
 
 Signed Commits
 ~~~~~~~~~~~~~~
 We add a new field "gpgsig-sha256" to the commit object format to allow
 signing commits without relying on SHA-1. It is similar to the
-existing "gpgsig" field. Its signed payload is the sha256-content of the
+existing "gpgsig" field. Its signed payload is the SHA-256 content of the
 commit object with any "gpgsig" and "gpgsig-sha256" fields removed.
 
 This means commits can be signed
+
 1. using SHA-1 only, as in existing signed commit objects
 2. using both SHA-1 and SHA-256, by using both gpgsig-sha256 and gpgsig
    fields.
@@ -404,10 +429,11 @@ Signed Tags
 ~~~~~~~~~~~
 We add a new field "gpgsig-sha256" to the tag object format to allow
 signing tags without relying on SHA-1. Its signed payload is the
-sha256-content of the tag with its gpgsig-sha256 field and "-----BEGIN PGP
+SHA-256 content of the tag with its gpgsig-sha256 field and "-----BEGIN PGP
 SIGNATURE-----" delimited in-body signature removed.
 
 This means tags can be signed
+
 1. using SHA-1 only, as in existing signed tag objects
 2. using both SHA-1 and SHA-256, by using gpgsig-sha256 and an in-body
    signature.
@@ -415,11 +441,11 @@ This means tags can be signed
 
 Mergetag embedding
 ~~~~~~~~~~~~~~~~~~
-The mergetag field in the sha1-content of a commit contains the
-sha1-content of a tag that was merged by that commit.
+The mergetag field in the SHA-1 content of a commit contains the
+SHA-1 content of a tag that was merged by that commit.
 
-The mergetag field in the sha256-content of the same commit contains the
-sha256-content of the same tag.
+The mergetag field in the SHA-256 content of the same commit contains the
+SHA-256 content of the same tag.
 
 Submodules
 ~~~~~~~~~~
@@ -494,7 +520,7 @@ Caveats
 -------
 Invalid objects
 ~~~~~~~~~~~~~~~
-The conversion from sha1-content to sha256-content retains any
+The conversion from SHA-1 content to SHA-256 content retains any
 brokenness in the original object (e.g., tree entry modes encoded with
 leading 0, tree objects whose paths are not sorted correctly, and
 commit objects without an author or committer). This is a deliberate
@@ -513,15 +539,15 @@ allow lifting this restriction.
 
 Alternates
 ~~~~~~~~~~
-For the same reason, a sha256 repository cannot borrow objects from a
-sha1 repository using objects/info/alternates or
+For the same reason, a SHA-256 repository cannot borrow objects from a
+SHA-1 repository using objects/info/alternates or
 $GIT_ALTERNATE_OBJECT_REPOSITORIES.
 
 git notes
 ~~~~~~~~~
-The "git notes" tool annotates objects using their sha1-name as key.
+The "git notes" tool annotates objects using their SHA-1 name as key.
 This design does not describe a way to migrate notes trees to use
-sha256-names. That migration is expected to happen separately (for
+SHA-256 names. That migration is expected to happen separately (for
 example using a file at the root of the notes tree to describe which
 hash it uses).
 
@@ -555,7 +581,7 @@ unclear:
 
        Git 2.12
 
-Does this mean Git v2.12.0 is the commit with sha1-name
+Does this mean Git v2.12.0 is the commit with SHA-1 name
 e7e07d5a4fcc2a203d9873968ad3e6bd4d7419d7 or the commit with
 new-40-digit-hash-name e7e07d5a4fcc2a203d9873968ad3e6bd4d7419d7?
 
@@ -598,44 +624,12 @@ The user can also explicitly specify which format to use for a
 particular revision specifier and for output, overriding the mode. For
 example:
 
-git --output-format=sha1 log abac87a^{sha1}..f787cac^{sha256}
-
-Choice of Hash
---------------
-In early 2005, around the time that Git was written, Xiaoyun Wang,
-Yiqun Lisa Yin, and Hongbo Yu announced an attack finding SHA-1
-collisions in 2^69 operations. In August they published details.
-Luckily, no practical demonstrations of a collision in full SHA-1 were
-published until 10 years later, in 2017.
-
-Git v2.13.0 and later subsequently moved to a hardened SHA-1
-implementation by default that mitigates the SHAttered attack, but
-SHA-1 is still believed to be weak.
-
-The hash to replace this hardened SHA-1 should be stronger than SHA-1
-was: we would like it to be trustworthy and useful in practice for at
-least 10 years.
-
-Some other relevant properties:
-
-1. A 256-bit hash (long enough to match common security practice; not
-   excessively long to hurt performance and disk usage).
-
-2. High quality implementations should be widely available (e.g., in
-   OpenSSL and Apple CommonCrypto).
-
-3. The hash function's properties should match Git's needs (e.g. Git
-   requires collision and 2nd preimage resistance and does not require
-   length extension resistance).
-
-4. As a tiebreaker, the hash should be fast to compute (fortunately
-   many contenders are faster than SHA-1).
-
-We choose SHA-256.
+    git --output-format=sha1 log abac87a^{sha1}..f787cac^{sha256}
 
 Transition plan
 ---------------
 Some initial steps can be implemented independently of one another:
+
 - adding a hash function API (vtable)
 - teaching fsck to tolerate the gpgsig-sha256 field
 - excluding gpgsig-* from the fields copied by "git commit --amend"
@@ -647,9 +641,9 @@ Some initial steps can be implemented independently of one another:
 - introducing index v3
 - adding support for the PSRC field and safer object pruning
 
-
 The first user-visible change is the introduction of the objectFormat
 extension (without compatObjectFormat). This requires:
+
 - teaching fsck about this mode of operation
 - using the hash function API (vtable) when computing object names
 - signing objects and verifying signatures
@@ -657,6 +651,7 @@ extension (without compatObjectFormat). This requires:
   repository
 
 Next comes introduction of compatObjectFormat:
+
 - implementing the loose-object-idx
 - translating object names between object formats
 - translating object content between object formats
@@ -669,10 +664,11 @@ Next comes introduction of compatObjectFormat:
   "Object names on the command line" above)
 
 The next step is supporting fetches and pushes to SHA-1 repositories:
+
 - allow pushes to a repository using the compat format
 - generate a topologically sorted list of the SHA-1 names of fetched
   objects
-- convert the fetched packfile to sha256 format and generate an idx
+- convert the fetched packfile to SHA-256 format and generate an idx
   file
 - re-sort to match the order of objects in the fetched packfile
 
@@ -734,6 +730,7 @@ Using hash functions in parallel
 Objects newly created would be addressed by the new hash, but inside
 such an object (e.g. commit) it is still possible to address objects
 using the old hash function.
+
 * You cannot trust its history (needed for bisectability) in the
   future without further work
 * Maintenance burden as the number of supported hash functions grows
@@ -743,36 +740,38 @@ using the old hash function.
 Signed objects with multiple hashes
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Instead of introducing the gpgsig-sha256 field in commit and tag objects
-for sha256-content based signatures, an earlier version of this design
-added "hash sha256 <sha256-name>" fields to strengthen the existing
-sha1-content based signatures.
+for SHA-256 content based signatures, an earlier version of this design
+added "hash sha256 <SHA-256 name>" fields to strengthen the existing
+SHA-1 content based signatures.
 
 In other words, a single signature was used to attest to the object
 content using both hash functions. This had some advantages:
+
 * Using one signature instead of two speeds up the signing process.
 * Having one signed payload with both hashes allows the signer to
-  attest to the sha1-name and sha256-name referring to the same object.
+  attest to the SHA-1 name and SHA-256 name referring to the same object.
 * All users consume the same signature. Broken signatures are likely
   to be detected quickly using current versions of git.
 
 However, it also came with disadvantages:
-* Verifying a signed object requires access to the sha1-names of all
+
+* Verifying a signed object requires access to the SHA-1 names of all
   objects it references, even after the transition is complete and
   translation table is no longer needed for anything else. To support
-  this, the design added fields such as "hash sha1 tree <sha1-name>"
-  and "hash sha1 parent <sha1-name>" to the sha256-content of a signed
+  this, the design added fields such as "hash sha1 tree <SHA-1 name>"
+  and "hash sha1 parent <SHA-1 name>" to the SHA-256 content of a signed
   commit, complicating the conversion process.
-* Allowing signed objects without a sha1 (for after the transition is
+* Allowing signed objects without a SHA-1 (for after the transition is
   complete) complicated the design further, requiring a "nohash sha1"
-  field to suppress including "hash sha1" fields in the sha256-content
+  field to suppress including "hash sha1" fields in the SHA-256 content
   and signed payload.
 
 Lazily populated translation table
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 Some of the work of building the translation table could be deferred to
 push time, but that would significantly complicate and slow down pushes.
-Calculating the sha1-name at object creation time at the same time it is
-being streamed to disk and having its sha256-name calculated should be
+Calculating the SHA-1 name at object creation time at the same time it is
+being streamed to disk and having its SHA-256 name calculated should be
 an acceptable cost.
 
 Document History
@@ -782,18 +781,19 @@ Document History
 bmwill@google.com, jonathantanmy@google.com, jrnieder@gmail.com,
 sbeller@google.com
 
-Initial version sent to
-http://lore.kernel.org/git/20170304011251.GA26789@aiede.mtv.corp.google.com
+* Initial version sent to https://lore.kernel.org/git/20170304011251.GA26789@aiede.mtv.corp.google.com
 
 2017-03-03 jrnieder@gmail.com
 Incorporated suggestions from jonathantanmy and sbeller:
-* describe purpose of signed objects with each hash type
-* redefine signed object verification using object content under the
+
+* Describe purpose of signed objects with each hash type
+* Redefine signed object verification using object content under the
   first hash function
 
 2017-03-06 jrnieder@gmail.com
+
 * Use SHA3-256 instead of SHA2 (thanks, Linus and brian m. carlson).[1][2]
-* Make sha3-based signatures a separate field, avoiding the need for
+* Make SHA3-based signatures a separate field, avoiding the need for
   "hash" and "nohash" fields (thanks to peff[3]).
 * Add a sorting phase to fetch (thanks to Junio for noticing the need
   for this).
@@ -805,23 +805,26 @@ Incorporated suggestions from jonathantanmy and sbeller:
   especially Junio).
 
 2017-09-27 jrnieder@gmail.com, sbeller@google.com
-* use placeholder NewHash instead of SHA3-256
-* describe criteria for picking a hash function.
-* include a transition plan (thanks especially to Brandon Williams
+
+* Use placeholder NewHash instead of SHA3-256
+* Describe criteria for picking a hash function.
+* Include a transition plan (thanks especially to Brandon Williams
   for fleshing these ideas out)
-* define the translation table (thanks, Shawn Pearce[5], Jonathan
+* Define the translation table (thanks, Shawn Pearce[5], Jonathan
   Tan, and Masaya Suzuki)
-* avoid loose object overhead by packing more aggressively in
+* Avoid loose object overhead by packing more aggressively in
   "git gc --auto"
 
 Later history:
 
- See the history of this file in git.git for the history of subsequent
- edits. This document history is no longer being maintained as it
- would now be superfluous to the commit log
+* See the history of this file in git.git for the history of subsequent
+  edits. This document history is no longer being maintained as it
+  would now be superfluous to the commit log
+
+References:
 
-[1] http://lore.kernel.org/git/CA+55aFzJtejiCjV0e43+9oR3QuJK2PiFiLQemytoLpyJWe6P9w@mail.gmail.com/
-[2] http://lore.kernel.org/git/CA+55aFz+gkAsDZ24zmePQuEs1XPS9BP_s8O7Q4wQ7LV7X5-oDA@mail.gmail.com/
-[3] http://lore.kernel.org/git/20170306084353.nrns455dvkdsfgo5@sigill.intra.peff.net/
-[4] http://lore.kernel.org/git/20170304224936.rqqtkdvfjgyezsht@genre.crustytoothpaste.net
-[5] https://lore.kernel.org/git/CAJo=hJtoX9=AyLHHpUJS7fueV9ciZ_MNpnEPHUz8Whui6g9F0A@mail.gmail.com/
+ [1] https://lore.kernel.org/git/CA+55aFzJtejiCjV0e43+9oR3QuJK2PiFiLQemytoLpyJWe6P9w@mail.gmail.com/
+ [2] https://lore.kernel.org/git/CA+55aFz+gkAsDZ24zmePQuEs1XPS9BP_s8O7Q4wQ7LV7X5-oDA@mail.gmail.com/
+ [3] https://lore.kernel.org/git/20170306084353.nrns455dvkdsfgo5@sigill.intra.peff.net/
+ [4] https://lore.kernel.org/git/20170304224936.rqqtkdvfjgyezsht@genre.crustytoothpaste.net
+ [5] https://lore.kernel.org/git/CAJo=hJtoX9=AyLHHpUJS7fueV9ciZ_MNpnEPHUz8Whui6g9F0A@mail.gmail.com/
index 96d2fc589f817afe9cccffc01fb45e423eacfb9c..8833b71c8b93a65e74e0d92694f984ebc41fa783 100644 (file)
@@ -274,6 +274,26 @@ Pack file entry: <+
 
     Index checksum of all of the above.
 
+== pack-*.rev files have the format:
+
+  - A 4-byte magic number '0x52494458' ('RIDX').
+
+  - A 4-byte version identifier (= 1).
+
+  - A 4-byte hash function identifier (= 1 for SHA-1, 2 for SHA-256).
+
+  - A table of index positions (one per packed object, num_objects in
+    total, each a 4-byte unsigned integer in network order), sorted by
+    their corresponding offsets in the packfile.
+
+  - A trailer, containing a:
+
+    checksum of the corresponding packfile, and
+
+    a checksum of all of the above.
+
+All 4-byte numbers are in network order.
+
 == multi-pack-index (MIDX) files have the following format:
 
 The multi-pack-index files refer to multiple pack-files and loose objects.
index 85daeb5d9e77f8d597cb7ce7d842598133b7b2a6..a7c806a73e0b5d74069e9c5b208c5f43021f10e5 100644 (file)
@@ -33,8 +33,8 @@ In protocol v2 these special packets will have the following semantics:
 
   * '0000' Flush Packet (flush-pkt) - indicates the end of a message
   * '0001' Delimiter Packet (delim-pkt) - separates sections of a message
-  * '0002' Message Packet (response-end-pkt) - indicates the end of a response
-    for stateless connections
+  * '0002' Response End Packet (response-end-pkt) - indicates the end of a
+    response for stateless connections
 
 Initial Client Request
 ----------------------
@@ -192,11 +192,20 @@ ls-refs takes in the following arguments:
        When specified, only references having a prefix matching one of
        the provided prefixes are displayed.
 
+If the 'unborn' feature is advertised the following argument can be
+included in the client's request.
+
+    unborn
+       The server will send information about HEAD even if it is a symref
+       pointing to an unborn branch in the form "unborn HEAD
+       symref-target:<target>".
+
 The output of ls-refs is as follows:
 
     output = *ref
             flush-pkt
-    ref = PKT-LINE(obj-id SP refname *(SP ref-attribute) LF)
+    obj-id-or-unborn = (obj-id | "unborn")
+    ref = PKT-LINE(obj-id-or-unborn SP refname *(SP ref-attribute) LF)
     ref-attribute = (symref | peeled)
     symref = "symref-target:" symref-target
     peeled = "peeled:" obj-id
index 709eb713a3ba469f39ef5b4536583a84cc0c4a52..d69e13335d9c79bb2f8bd1b59bca42106e32b378 100644 (file)
@@ -21,16 +21,15 @@ static GIT_PATH_FUNC(git_path_bisect_first_parent, "BISECT_FIRST_PARENT")
 
 static const char * const git_bisect_helper_usage[] = {
        N_("git bisect--helper --bisect-reset [<commit>]"),
-       N_("git bisect--helper --bisect-write [--no-log] <state> <revision> <good_term> <bad_term>"),
-       N_("git bisect--helper --bisect-check-and-set-terms <command> <good_term> <bad_term>"),
        N_("git bisect--helper --bisect-next-check <good_term> <bad_term> [<term>]"),
        N_("git bisect--helper --bisect-terms [--term-good | --term-old | --term-bad | --term-new]"),
        N_("git bisect--helper --bisect-start [--term-{new,bad}=<term> --term-{old,good}=<term>]"
                                            " [--no-checkout] [--first-parent] [<bad> [<good>...]] [--] [<paths>...]"),
        N_("git bisect--helper --bisect-next"),
-       N_("git bisect--helper --bisect-auto-next"),
        N_("git bisect--helper --bisect-state (bad|new) [<rev>]"),
        N_("git bisect--helper --bisect-state (good|old) [<rev>...]"),
+       N_("git bisect--helper --bisect-replay <filename>"),
+       N_("git bisect--helper --bisect-skip [(<rev>|<range>)...]"),
        NULL
 };
 
@@ -904,28 +903,148 @@ static enum bisect_error bisect_state(struct bisect_terms *terms, const char **a
        return bisect_auto_next(terms, NULL);
 }
 
+static enum bisect_error bisect_log(void)
+{
+       int fd, status;
+       const char* filename = git_path_bisect_log();
+
+       if (is_empty_or_missing_file(filename))
+               return error(_("We are not bisecting."));
+
+       fd = open(filename, O_RDONLY);
+       if (fd < 0)
+               return BISECT_FAILED;
+
+       status = copy_fd(fd, STDOUT_FILENO);
+       close(fd);
+       return status ? BISECT_FAILED : BISECT_OK;
+}
+
+static int process_replay_line(struct bisect_terms *terms, struct strbuf *line)
+{
+       const char *p = line->buf + strspn(line->buf, " \t");
+       char *word_end, *rev;
+
+       if ((!skip_prefix(p, "git bisect", &p) &&
+       !skip_prefix(p, "git-bisect", &p)) || !isspace(*p))
+               return 0;
+       p += strspn(p, " \t");
+
+       word_end = (char *)p + strcspn(p, " \t");
+       rev = word_end + strspn(word_end, " \t");
+       *word_end = '\0'; /* NUL-terminate the word */
+
+       get_terms(terms);
+       if (check_and_set_terms(terms, p))
+               return -1;
+
+       if (!strcmp(p, "start")) {
+               struct strvec argv = STRVEC_INIT;
+               int res;
+               sq_dequote_to_strvec(rev, &argv);
+               res = bisect_start(terms, argv.v, argv.nr);
+               strvec_clear(&argv);
+               return res;
+       }
+
+       if (one_of(p, terms->term_good,
+          terms->term_bad, "skip", NULL))
+               return bisect_write(p, rev, terms, 0);
+
+       if (!strcmp(p, "terms")) {
+               struct strvec argv = STRVEC_INIT;
+               int res;
+               sq_dequote_to_strvec(rev, &argv);
+               res = bisect_terms(terms, argv.nr == 1 ? argv.v[0] : NULL);
+               strvec_clear(&argv);
+               return res;
+       }
+       error(_("'%s'?? what are you talking about?"), p);
+
+       return -1;
+}
+
+static enum bisect_error bisect_replay(struct bisect_terms *terms, const char *filename)
+{
+       FILE *fp = NULL;
+       enum bisect_error res = BISECT_OK;
+       struct strbuf line = STRBUF_INIT;
+
+       if (is_empty_or_missing_file(filename))
+               return error(_("cannot read file '%s' for replaying"), filename);
+
+       if (bisect_reset(NULL))
+               return BISECT_FAILED;
+
+       fp = fopen(filename, "r");
+       if (!fp)
+               return BISECT_FAILED;
+
+       while ((strbuf_getline(&line, fp) != EOF) && !res)
+               res = process_replay_line(terms, &line);
+
+       strbuf_release(&line);
+       fclose(fp);
+
+       if (res)
+               return BISECT_FAILED;
+
+       return bisect_auto_next(terms, NULL);
+}
+
+static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **argv, int argc)
+{
+       int i;
+       enum bisect_error res;
+       struct strvec argv_state = STRVEC_INIT;
+
+       strvec_push(&argv_state, "skip");
+
+       for (i = 0; i < argc; i++) {
+               const char *dotdot = strstr(argv[i], "..");
+
+               if (dotdot) {
+                       struct rev_info revs;
+                       struct commit *commit;
+
+                       init_revisions(&revs, NULL);
+                       setup_revisions(2, argv + i - 1, &revs, NULL);
+
+                       if (prepare_revision_walk(&revs))
+                               die(_("revision walk setup failed\n"));
+                       while ((commit = get_revision(&revs)) != NULL)
+                               strvec_push(&argv_state,
+                                               oid_to_hex(&commit->object.oid));
+
+                       reset_revision_walk();
+               } else {
+                       strvec_push(&argv_state, argv[i]);
+               }
+       }
+       res = bisect_state(terms, argv_state.v, argv_state.nr);
+
+       strvec_clear(&argv_state);
+       return res;
+}
+
 int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
 {
        enum {
                BISECT_RESET = 1,
-               BISECT_WRITE,
-               CHECK_AND_SET_TERMS,
                BISECT_NEXT_CHECK,
                BISECT_TERMS,
                BISECT_START,
                BISECT_AUTOSTART,
                BISECT_NEXT,
-               BISECT_AUTO_NEXT,
-               BISECT_STATE
+               BISECT_STATE,
+               BISECT_LOG,
+               BISECT_REPLAY,
+               BISECT_SKIP
        } cmdmode = 0;
        int res = 0, nolog = 0;
        struct option options[] = {
                OPT_CMDMODE(0, "bisect-reset", &cmdmode,
                         N_("reset the bisection state"), BISECT_RESET),
-               OPT_CMDMODE(0, "bisect-write", &cmdmode,
-                        N_("write out the bisection state in BISECT_LOG"), BISECT_WRITE),
-               OPT_CMDMODE(0, "check-and-set-terms", &cmdmode,
-                        N_("check and set terms in a bisection state"), CHECK_AND_SET_TERMS),
                OPT_CMDMODE(0, "bisect-next-check", &cmdmode,
                         N_("check whether bad or good terms exist"), BISECT_NEXT_CHECK),
                OPT_CMDMODE(0, "bisect-terms", &cmdmode,
@@ -934,10 +1053,14 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                         N_("start the bisect session"), BISECT_START),
                OPT_CMDMODE(0, "bisect-next", &cmdmode,
                         N_("find the next bisection commit"), BISECT_NEXT),
-               OPT_CMDMODE(0, "bisect-auto-next", &cmdmode,
-                        N_("verify the next bisection state then checkout the next bisection commit"), BISECT_AUTO_NEXT),
                OPT_CMDMODE(0, "bisect-state", &cmdmode,
                         N_("mark the state of ref (or refs)"), BISECT_STATE),
+               OPT_CMDMODE(0, "bisect-log", &cmdmode,
+                        N_("list the bisection steps so far"), BISECT_LOG),
+               OPT_CMDMODE(0, "bisect-replay", &cmdmode,
+                        N_("replay the bisection process from the given file"), BISECT_REPLAY),
+               OPT_CMDMODE(0, "bisect-skip", &cmdmode,
+                        N_("skip some commits for checkout"), BISECT_SKIP),
                OPT_BOOL(0, "no-log", &nolog,
                         N_("no log for BISECT_WRITE")),
                OPT_END()
@@ -955,18 +1078,7 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
        case BISECT_RESET:
                if (argc > 1)
                        return error(_("--bisect-reset requires either no argument or a commit"));
-               return !!bisect_reset(argc ? argv[0] : NULL);
-       case BISECT_WRITE:
-               if (argc != 4 && argc != 5)
-                       return error(_("--bisect-write requires either 4 or 5 arguments"));
-               set_terms(&terms, argv[3], argv[2]);
-               res = bisect_write(argv[0], argv[1], &terms, nolog);
-               break;
-       case CHECK_AND_SET_TERMS:
-               if (argc != 3)
-                       return error(_("--check-and-set-terms requires 3 arguments"));
-               set_terms(&terms, argv[2], argv[1]);
-               res = check_and_set_terms(&terms, argv[0]);
+               res = bisect_reset(argc ? argv[0] : NULL);
                break;
        case BISECT_NEXT_CHECK:
                if (argc != 2 && argc != 3)
@@ -989,17 +1101,26 @@ int cmd_bisect__helper(int argc, const char **argv, const char *prefix)
                get_terms(&terms);
                res = bisect_next(&terms, prefix);
                break;
-       case BISECT_AUTO_NEXT:
-               if (argc)
-                       return error(_("--bisect-auto-next requires 0 arguments"));
-               get_terms(&terms);
-               res = bisect_auto_next(&terms, prefix);
-               break;
        case BISECT_STATE:
                set_terms(&terms, "bad", "good");
                get_terms(&terms);
                res = bisect_state(&terms, argv, argc);
                break;
+       case BISECT_LOG:
+               if (argc)
+                       return error(_("--bisect-log requires 0 arguments"));
+               res = bisect_log();
+               break;
+       case BISECT_REPLAY:
+               if (argc != 1)
+                       return error(_("no logfile given"));
+               set_terms(&terms, "bad", "good");
+               res = bisect_replay(&terms, argv[0]);
+               break;
+       case BISECT_SKIP:
+               set_terms(&terms, "bad", "good");
+               res = bisect_skip(&terms, argv, argc);
+               break;
        default:
                BUG("unknown subcommand %d", cmdmode);
        }
index b66e938022bc5fdf61d9910c274c08a79084648e..641523ff9af693bcbef8e77e66b3a5a607f7219f 100644 (file)
@@ -425,13 +425,11 @@ static void setup_default_color_by_age(void)
        parse_color_fields("blue,12 month ago,white,1 month ago,red");
 }
 
-static void determine_line_heat(struct blame_entry *ent, const char **dest_color)
+static void determine_line_heat(struct commit_info *ci, const char **dest_color)
 {
        int i = 0;
-       struct commit_info ci;
-       get_commit_info(ent->suspect->commit, &ci, 1);
 
-       while (i < colorfield_nr && ci.author_time > colorfield[i].hop)
+       while (i < colorfield_nr && ci->author_time > colorfield[i].hop)
                i++;
 
        *dest_color = colorfield[i].col;
@@ -453,7 +451,7 @@ static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int
        cp = blame_nth_line(sb, ent->lno);
 
        if (opt & OUTPUT_SHOW_AGE_WITH_COLOR) {
-               determine_line_heat(ent, &default_color);
+               determine_line_heat(&ci, &default_color);
                color = default_color;
                reset = GIT_COLOR_RESET;
        }
index 4bbfc92dce5a0e71e4389c16fd096f4a22fef4a1..023e49e271c2e3ed2dc4c5f1f27d39000353aeb7 100644 (file)
@@ -23,22 +23,35 @@ static struct checkout state = CHECKOUT_INIT;
 static void write_tempfile_record(const char *name, const char *prefix)
 {
        int i;
+       int have_tempname = 0;
 
        if (CHECKOUT_ALL == checkout_stage) {
-               for (i = 1; i < 4; i++) {
-                       if (i > 1)
-                               putchar(' ');
-                       if (topath[i][0])
-                               fputs(topath[i], stdout);
-                       else
-                               putchar('.');
+               for (i = 1; i < 4; i++)
+                       if (topath[i][0]) {
+                               have_tempname = 1;
+                               break;
+                       }
+
+               if (have_tempname) {
+                       for (i = 1; i < 4; i++) {
+                               if (i > 1)
+                                       putchar(' ');
+                               if (topath[i][0])
+                                       fputs(topath[i], stdout);
+                               else
+                                       putchar('.');
+                       }
                }
-       } else
+       } else if (topath[checkout_stage][0]) {
+               have_tempname = 1;
                fputs(topath[checkout_stage], stdout);
+       }
 
-       putchar('\t');
-       write_name_quoted_relative(name, prefix, stdout,
-                                  nul_term_line ? '\0' : '\n');
+       if (have_tempname) {
+               putchar('\t');
+               write_name_quoted_relative(name, prefix, stdout,
+                                          nul_term_line ? '\0' : '\n');
+       }
 
        for (i = 0; i < 4; i++) {
                topath[i][0] = 0;
index e335734b4cfd32560b5d0cfa8a60426fa5c9514c..51e844a2de0a236f3ecd0305b3a5726aed80f5e6 100644 (file)
@@ -979,7 +979,8 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        int err = 0, complete_refs_before_fetch = 1;
        int submodule_progress;
 
-       struct strvec ref_prefixes = STRVEC_INIT;
+       struct transport_ls_refs_options transport_ls_refs_options =
+               TRANSPORT_LS_REFS_OPTIONS_INIT;
 
        packet_trace_identity("clone");
 
@@ -1257,14 +1258,17 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport->smart_options->check_self_contained_and_connected = 1;
 
 
-       strvec_push(&ref_prefixes, "HEAD");
-       refspec_ref_prefixes(&remote->fetch, &ref_prefixes);
+       strvec_push(&transport_ls_refs_options.ref_prefixes, "HEAD");
+       refspec_ref_prefixes(&remote->fetch,
+                            &transport_ls_refs_options.ref_prefixes);
        if (option_branch)
-               expand_ref_prefix(&ref_prefixes, option_branch);
+               expand_ref_prefix(&transport_ls_refs_options.ref_prefixes,
+                                 option_branch);
        if (!option_no_tags)
-               strvec_push(&ref_prefixes, "refs/tags/");
+               strvec_push(&transport_ls_refs_options.ref_prefixes,
+                           "refs/tags/");
 
-       refs = transport_get_remote_refs(transport, &ref_prefixes);
+       refs = transport_get_remote_refs(transport, &transport_ls_refs_options);
 
        if (refs) {
                int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
@@ -1326,8 +1330,19 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                remote_head = NULL;
                option_no_checkout = 1;
                if (!option_bare) {
-                       const char *branch = git_default_branch_name(0);
-                       char *ref = xstrfmt("refs/heads/%s", branch);
+                       const char *branch;
+                       char *ref;
+
+                       if (transport_ls_refs_options.unborn_head_target &&
+                           skip_prefix(transport_ls_refs_options.unborn_head_target,
+                                       "refs/heads/", &branch)) {
+                               ref = transport_ls_refs_options.unborn_head_target;
+                               transport_ls_refs_options.unborn_head_target = NULL;
+                               create_symref("HEAD", ref, reflog_msg.buf);
+                       } else {
+                               branch = git_default_branch_name(0);
+                               ref = xstrfmt("refs/heads/%s", branch);
+                       }
 
                        install_branch_config(0, branch, remote_name, ref);
                        free(ref);
@@ -1380,6 +1395,7 @@ cleanup:
        strbuf_release(&key);
        junk_mode = JUNK_LEAVE_ALL;
 
-       strvec_clear(&ref_prefixes);
+       strvec_clear(&transport_ls_refs_options.ref_prefixes);
+       free(transport_ls_refs_options.unborn_head_target);
        return err;
 }
index e037efb07effc5432b05eda2f36c0bbb591d3a29..70103c40952093100e41361e15ab6dbe54ef0268 100644 (file)
@@ -36,7 +36,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
         */
        rev.diffopt.ita_invisible_in_index = 1;
 
-       precompose_argv(argc, argv);
+       prefix = precompose_argv_prefix(argc, argv, prefix);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        while (1 < argc && argv[1][0] == '-') {
index 06635e8fb26f0e33948c1c4b56d2533651174223..176fe7ff2b4e15b4a95ffc3019528318af84b42a 100644 (file)
@@ -25,7 +25,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
        git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
        repo_init_revisions(the_repository, &rev, prefix);
        rev.abbrev = 0;
-       precompose_argv(argc, argv);
+       prefix = precompose_argv_prefix(argc, argv, prefix);
 
        argc = setup_revisions(argc, argv, &rev, NULL);
        for (i = 1; i < argc; i++) {
index b6a9a9328e88ab247cf400b71767997b5fc8ef14..f33d30d57bff2ef8923ea9649a5afd4d49eb0574 100644 (file)
@@ -126,7 +126,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        memset(&s_r_opt, 0, sizeof(s_r_opt));
        s_r_opt.tweak = diff_tree_tweak_rev;
 
-       precompose_argv(argc, argv);
+       prefix = precompose_argv_prefix(argc, argv, prefix);
        argc = setup_revisions(argc, argv, opt, &s_r_opt);
 
        memset(&w, 0, sizeof(w));
index f1b88c7389eb0ee5c32d6e41215d894f1303a3e9..617b9a4101dbd8255983664d403316a238e0c952 100644 (file)
@@ -453,7 +453,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 
        init_diff_ui_defaults();
        git_config(git_diff_ui_config, NULL);
-       precompose_argv(argc, argv);
+       prefix = precompose_argv_prefix(argc, argv, prefix);
 
        repo_init_revisions(the_repository, &rev, prefix);
 
index 58b7c1fbdc47b7f3bf866e1e9bcdf924a10e8033..c2d96f4c89ab0fa7207f74a17d0bdf94f8bd3780 100644 (file)
@@ -220,7 +220,8 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        version = discover_version(&reader);
        switch (version) {
        case protocol_v2:
-               get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL, args.stateless_rpc);
+               get_remote_refs(fd[1], &reader, &ref, 0, NULL, NULL,
+                               args.stateless_rpc);
                break;
        case protocol_v1:
        case protocol_v0:
index 91f3d20696d37a4827872f735978a437e8d2b230..0b90de87c7a2ade9e11e67c8d59338eebdc131a7 100644 (file)
@@ -1455,7 +1455,8 @@ static int do_fetch(struct transport *transport,
        int autotags = (transport->remote->fetch_tags == 1);
        int retcode = 0;
        const struct ref *remote_refs;
-       struct strvec ref_prefixes = STRVEC_INIT;
+       struct transport_ls_refs_options transport_ls_refs_options =
+               TRANSPORT_LS_REFS_OPTIONS_INIT;
        int must_list_refs = 1;
 
        if (tags == TAGS_DEFAULT) {
@@ -1475,7 +1476,7 @@ static int do_fetch(struct transport *transport,
        if (rs->nr) {
                int i;
 
-               refspec_ref_prefixes(rs, &ref_prefixes);
+               refspec_ref_prefixes(rs, &transport_ls_refs_options.ref_prefixes);
 
                /*
                 * We can avoid listing refs if all of them are exact
@@ -1489,22 +1490,25 @@ static int do_fetch(struct transport *transport,
                        }
                }
        } else if (transport->remote && transport->remote->fetch.nr)
-               refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
+               refspec_ref_prefixes(&transport->remote->fetch,
+                                    &transport_ls_refs_options.ref_prefixes);
 
        if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
                must_list_refs = 1;
-               if (ref_prefixes.nr)
-                       strvec_push(&ref_prefixes, "refs/tags/");
+               if (transport_ls_refs_options.ref_prefixes.nr)
+                       strvec_push(&transport_ls_refs_options.ref_prefixes,
+                                   "refs/tags/");
        }
 
        if (must_list_refs) {
                trace2_region_enter("fetch", "remote_refs", the_repository);
-               remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+               remote_refs = transport_get_remote_refs(transport,
+                                                       &transport_ls_refs_options);
                trace2_region_leave("fetch", "remote_refs", the_repository);
        } else
                remote_refs = NULL;
 
-       strvec_clear(&ref_prefixes);
+       strvec_clear(&transport_ls_refs_options.ref_prefixes);
 
        ref_map = get_ref_map(transport->remote, remote_refs, rs,
                              tags, &autotags);
index 4c40594d660ebb21c2cf0223b21af88ea81f872c..6db9cb39e6797f9f3bea580916ec4e855e32cea3 100644 (file)
@@ -54,7 +54,6 @@ static const char *prune_worktrees_expire = "3.months.ago";
 static unsigned long big_pack_threshold;
 static unsigned long max_delta_cache_size = DEFAULT_DELTA_CACHE_SIZE;
 
-static struct strvec pack_refs_cmd = STRVEC_INIT;
 static struct strvec reflog = STRVEC_INIT;
 static struct strvec repack = STRVEC_INIT;
 static struct strvec prune = STRVEC_INIT;
@@ -163,6 +162,15 @@ static void gc_config(void)
        git_config(git_default_config, NULL);
 }
 
+struct maintenance_run_opts;
+static int maintenance_task_pack_refs(MAYBE_UNUSED struct maintenance_run_opts *opts)
+{
+       struct strvec pack_refs_cmd = STRVEC_INIT;
+       strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
+
+       return run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD);
+}
+
 static int too_many_loose_objects(void)
 {
        /*
@@ -518,8 +526,8 @@ static void gc_before_repack(void)
        if (done++)
                return;
 
-       if (pack_refs && run_command_v_opt(pack_refs_cmd.v, RUN_GIT_CMD))
-               die(FAILED_RUN, pack_refs_cmd.v[0]);
+       if (pack_refs && maintenance_task_pack_refs(NULL))
+               die(FAILED_RUN, "pack-refs");
 
        if (prune_reflogs && run_command_v_opt(reflog.v, RUN_GIT_CMD))
                die(FAILED_RUN, reflog.v[0]);
@@ -556,7 +564,6 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
        if (argc == 2 && !strcmp(argv[1], "-h"))
                usage_with_options(builtin_gc_usage, builtin_gc_options);
 
-       strvec_pushl(&pack_refs_cmd, "pack-refs", "--all", "--prune", NULL);
        strvec_pushl(&reflog, "reflog", "expire", "--all", NULL);
        strvec_pushl(&repack, "repack", "-d", "-l", NULL);
        strvec_pushl(&prune, "prune", "--expire", NULL);
@@ -1224,6 +1231,7 @@ enum maintenance_task_label {
        TASK_INCREMENTAL_REPACK,
        TASK_GC,
        TASK_COMMIT_GRAPH,
+       TASK_PACK_REFS,
 
        /* Leave as final value */
        TASK__COUNT
@@ -1255,6 +1263,11 @@ static struct maintenance_task tasks[] = {
                maintenance_task_commit_graph,
                should_write_commit_graph,
        },
+       [TASK_PACK_REFS] = {
+               "pack-refs",
+               maintenance_task_pack_refs,
+               NULL,
+       },
 };
 
 static int compare_tasks_by_selection(const void *a_, const void *b_)
@@ -1339,6 +1352,8 @@ static void initialize_maintenance_strategy(void)
                tasks[TASK_INCREMENTAL_REPACK].schedule = SCHEDULE_DAILY;
                tasks[TASK_LOOSE_OBJECTS].enabled = 1;
                tasks[TASK_LOOSE_OBJECTS].schedule = SCHEDULE_DAILY;
+               tasks[TASK_PACK_REFS].enabled = 1;
+               tasks[TASK_PACK_REFS].schedule = SCHEDULE_WEEKLY;
        }
 }
 
index 55d06c951301b53b7db1bd0cb832e43c9793f1e9..e348e6bb3264693a88db5da4cd8f9e43a48a4023 100644 (file)
@@ -1152,6 +1152,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        if (!use_index && (untracked || cached))
                die(_("--cached or --untracked cannot be used with --no-index"));
 
+       if (untracked && cached)
+               die(_("--untracked cannot be used with --cached"));
+
        if (!use_index || untracked) {
                int use_exclude = (opt_exclude < 0) ? use_index : !!opt_exclude;
                hit = grep_directory(&opt, &pathspec, use_exclude, use_index);
index 557bd2f3480e606636af79bd4c0f6deff8b0a3c7..54f74c48741df84f35cc5269c332b51a347f7fa0 100644 (file)
@@ -17,7 +17,7 @@
 #include "promisor-remote.h"
 
 static const char index_pack_usage[] =
-"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
+"git index-pack [-v] [-o <index-file>] [--keep | --keep=<msg>] [--[no-]rev-index] [--verify] [--strict] (<pack-file> | --stdin [--fix-thin] [<pack-file>])";
 
 struct object_entry {
        struct pack_idx_entry idx;
@@ -1436,15 +1436,15 @@ static void fix_unresolved_deltas(struct hashfile *f)
        free(sorted_by_pos);
 }
 
-static const char *derive_filename(const char *pack_name, const char *suffix,
-                                  struct strbuf *buf)
+static const char *derive_filename(const char *pack_name, const char *strip,
+                                  const char *suffix, struct strbuf *buf)
 {
        size_t len;
-       if (!strip_suffix(pack_name, ".pack", &len))
-               die(_("packfile name '%s' does not end with '.pack'"),
-                   pack_name);
+       if (!strip_suffix(pack_name, strip, &len) || !len ||
+           pack_name[len - 1] != '.')
+               die(_("packfile name '%s' does not end with '.%s'"),
+                   pack_name, strip);
        strbuf_add(buf, pack_name, len);
-       strbuf_addch(buf, '.');
        strbuf_addstr(buf, suffix);
        return buf->buf;
 }
@@ -1459,7 +1459,7 @@ static void write_special_file(const char *suffix, const char *msg,
        int msg_len = strlen(msg);
 
        if (pack_name)
-               filename = derive_filename(pack_name, suffix, &name_buf);
+               filename = derive_filename(pack_name, "pack", suffix, &name_buf);
        else
                filename = odb_pack_name(&name_buf, hash, suffix);
 
@@ -1484,12 +1484,14 @@ static void write_special_file(const char *suffix, const char *msg,
 
 static void final(const char *final_pack_name, const char *curr_pack_name,
                  const char *final_index_name, const char *curr_index_name,
+                 const char *final_rev_index_name, const char *curr_rev_index_name,
                  const char *keep_msg, const char *promisor_msg,
                  unsigned char *hash)
 {
        const char *report = "pack";
        struct strbuf pack_name = STRBUF_INIT;
        struct strbuf index_name = STRBUF_INIT;
+       struct strbuf rev_index_name = STRBUF_INIT;
        int err;
 
        if (!from_stdin) {
@@ -1524,6 +1526,16 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
        } else
                chmod(final_index_name, 0444);
 
+       if (curr_rev_index_name) {
+               if (final_rev_index_name != curr_rev_index_name) {
+                       if (!final_rev_index_name)
+                               final_rev_index_name = odb_pack_name(&rev_index_name, hash, "rev");
+                       if (finalize_object_file(curr_rev_index_name, final_rev_index_name))
+                               die(_("cannot store reverse index file"));
+               } else
+                       chmod(final_rev_index_name, 0444);
+       }
+
        if (do_fsck_object) {
                struct packed_git *p;
                p = add_packed_git(final_index_name, strlen(final_index_name), 0);
@@ -1553,6 +1565,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                }
        }
 
+       strbuf_release(&rev_index_name);
        strbuf_release(&index_name);
        strbuf_release(&pack_name);
 }
@@ -1578,6 +1591,12 @@ static int git_index_pack_config(const char *k, const char *v, void *cb)
                }
                return 0;
        }
+       if (!strcmp(k, "pack.writereverseindex")) {
+               if (git_config_bool(k, v))
+                       opts->flags |= WRITE_REV;
+               else
+                       opts->flags &= ~WRITE_REV;
+       }
        return git_default_config(k, v, cb);
 }
 
@@ -1695,12 +1714,14 @@ static void show_pack_info(int stat_only)
 
 int cmd_index_pack(int argc, const char **argv, const char *prefix)
 {
-       int i, fix_thin_pack = 0, verify = 0, stat_only = 0;
+       int i, fix_thin_pack = 0, verify = 0, stat_only = 0, rev_index;
        const char *curr_index;
-       const char *index_name = NULL, *pack_name = NULL;
+       const char *curr_rev_index = NULL;
+       const char *index_name = NULL, *pack_name = NULL, *rev_index_name = NULL;
        const char *keep_msg = NULL;
        const char *promisor_msg = NULL;
        struct strbuf index_name_buf = STRBUF_INIT;
+       struct strbuf rev_index_name_buf = STRBUF_INIT;
        struct pack_idx_entry **idx_objects;
        struct pack_idx_option opts;
        unsigned char pack_hash[GIT_MAX_RAWSZ];
@@ -1727,6 +1748,11 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (prefix && chdir(prefix))
                die(_("Cannot come back to cwd"));
 
+       if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
+               rev_index = 1;
+       else
+               rev_index = !!(opts.flags & (WRITE_REV_VERIFY | WRITE_REV));
+
        for (i = 1; i < argc; i++) {
                const char *arg = argv[i];
 
@@ -1805,6 +1831,10 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
                                if (hash_algo == GIT_HASH_UNKNOWN)
                                        die(_("unknown hash algorithm '%s'"), arg);
                                repo_set_hash_algo(the_repository, hash_algo);
+                       } else if (!strcmp(arg, "--rev-index")) {
+                               rev_index = 1;
+                       } else if (!strcmp(arg, "--no-rev-index")) {
+                               rev_index = 0;
                        } else
                                usage(index_pack_usage);
                        continue;
@@ -1824,7 +1854,16 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        if (from_stdin && hash_algo)
                die(_("--object-format cannot be used with --stdin"));
        if (!index_name && pack_name)
-               index_name = derive_filename(pack_name, "idx", &index_name_buf);
+               index_name = derive_filename(pack_name, "pack", "idx", &index_name_buf);
+
+       opts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);
+       if (rev_index) {
+               opts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV;
+               if (index_name)
+                       rev_index_name = derive_filename(index_name,
+                                                        "idx", "rev",
+                                                        &rev_index_name_buf);
+       }
 
        if (verify) {
                if (!index_name)
@@ -1878,11 +1917,16 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
        for (i = 0; i < nr_objects; i++)
                idx_objects[i] = &objects[i].idx;
        curr_index = write_idx_file(index_name, idx_objects, nr_objects, &opts, pack_hash);
+       if (rev_index)
+               curr_rev_index = write_rev_file(rev_index_name, idx_objects,
+                                               nr_objects, pack_hash,
+                                               opts.flags);
        free(idx_objects);
 
        if (!verify)
                final(pack_name, curr_pack,
                      index_name, curr_index,
+                     rev_index_name, curr_rev_index,
                      keep_msg, promisor_msg,
                      pack_hash);
        else
@@ -1893,10 +1937,13 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)
 
        free(objects);
        strbuf_release(&index_name_buf);
+       strbuf_release(&rev_index_name_buf);
        if (pack_name == NULL)
                free((void *) curr_pack);
        if (index_name == NULL)
                free((void *) curr_index);
+       if (rev_index_name == NULL)
+               free((void *) curr_rev_index);
 
        /*
         * Let the caller know this pack is not self contained
index d0cbaaf68a083f0cfa3294804f606b5cb6dff5c4..f67b67d80ed1e8d7cd95982a8da96b435b8a452b 100644 (file)
@@ -307,10 +307,11 @@ static struct itimerval early_output_timer;
 
 static void log_show_early(struct rev_info *revs, struct commit_list *list)
 {
-       int i = revs->early_output, close_file = revs->diffopt.close_file;
+       int i = revs->early_output;
        int show_header = 1;
+       int no_free = revs->diffopt.no_free;
 
-       revs->diffopt.close_file = 0;
+       revs->diffopt.no_free = 0;
        sort_in_topological_order(&list, revs->sort_order);
        while (list && i) {
                struct commit *commit = list->item;
@@ -327,8 +328,8 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
                case commit_ignore:
                        break;
                case commit_error:
-                       if (close_file)
-                               fclose(revs->diffopt.file);
+                       revs->diffopt.no_free = no_free;
+                       diff_free(&revs->diffopt);
                        return;
                }
                list = list->next;
@@ -336,8 +337,8 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
 
        /* Did we already get enough commits for the early output? */
        if (!i) {
-               if (close_file)
-                       fclose(revs->diffopt.file);
+               revs->diffopt.no_free = 0;
+               diff_free(&revs->diffopt);
                return;
        }
 
@@ -401,7 +402,7 @@ static int cmd_log_walk(struct rev_info *rev)
 {
        struct commit *commit;
        int saved_nrl = 0;
-       int saved_dcctc = 0, close_file = rev->diffopt.close_file;
+       int saved_dcctc = 0;
 
        if (rev->early_output)
                setup_early_output();
@@ -417,7 +418,7 @@ static int cmd_log_walk(struct rev_info *rev)
         * and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
         * retain that state information if replacing rev->diffopt in this loop
         */
-       rev->diffopt.close_file = 0;
+       rev->diffopt.no_free = 1;
        while ((commit = get_revision(rev)) != NULL) {
                if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
                        /*
@@ -442,8 +443,8 @@ static int cmd_log_walk(struct rev_info *rev)
        }
        rev->diffopt.degraded_cc_to_c = saved_dcctc;
        rev->diffopt.needed_rename_limit = saved_nrl;
-       if (close_file)
-               fclose(rev->diffopt.file);
+       rev->diffopt.no_free = 0;
+       diff_free(&rev->diffopt);
 
        if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
            rev->diffopt.flags.check_failed) {
@@ -1223,14 +1224,20 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
                 */
                struct diff_options opts;
                struct strvec other_arg = STRVEC_INIT;
+               struct range_diff_options range_diff_opts = {
+                       .creation_factor = rev->creation_factor,
+                       .dual_color = 1,
+                       .diffopt = &opts,
+                       .other_arg = &other_arg
+               };
+
                diff_setup(&opts);
                opts.file = rev->diffopt.file;
                opts.use_color = rev->diffopt.use_color;
                diff_setup_done(&opts);
                fprintf_ln(rev->diffopt.file, "%s", rev->rdiff_title);
                get_notes_args(&other_arg, rev);
-               show_range_diff(rev->rdiff1, rev->rdiff2,
-                               rev->creation_factor, 1, &opts, &other_arg);
+               show_range_diff(rev->rdiff1, rev->rdiff2, &range_diff_opts);
                strvec_clear(&other_arg);
        }
 }
@@ -1672,7 +1679,7 @@ static void infer_range_diff_ranges(struct strbuf *r1,
                                    struct commit *head)
 {
        const char *head_oid = oid_to_hex(&head->object.oid);
-       int prev_is_range = !!strstr(prev, "..");
+       int prev_is_range = is_range_diff_range(prev);
 
        if (prev_is_range)
                strbuf_addstr(r1, prev);
@@ -1955,7 +1962,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                 * file, but but we must instruct it not to close after each
                 * diff.
                 */
-               rev.diffopt.close_file = 0;
+               rev.diffopt.no_free = 1;
        } else {
                int saved;
 
index 092917eca29b76f27bce31cbf6a5fff562fae7f0..ef604752a044a48609a10ee90a8ca487c0acfb2d 100644 (file)
@@ -45,7 +45,8 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        int show_symref_target = 0;
        const char *uploadpack = NULL;
        const char **pattern = NULL;
-       struct strvec ref_prefixes = STRVEC_INIT;
+       struct transport_ls_refs_options transport_options =
+               TRANSPORT_LS_REFS_OPTIONS_INIT;
        int i;
        struct string_list server_options = STRING_LIST_INIT_DUP;
 
@@ -94,9 +95,9 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        }
 
        if (flags & REF_TAGS)
-               strvec_push(&ref_prefixes, "refs/tags/");
+               strvec_push(&transport_options.ref_prefixes, "refs/tags/");
        if (flags & REF_HEADS)
-               strvec_push(&ref_prefixes, "refs/heads/");
+               strvec_push(&transport_options.ref_prefixes, "refs/heads/");
 
        remote = remote_get(dest);
        if (!remote) {
@@ -118,7 +119,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
        if (server_options.nr)
                transport->server_options = &server_options;
 
-       ref = transport_get_remote_refs(transport, &ref_prefixes);
+       ref = transport_get_remote_refs(transport, &transport_options);
        if (ref) {
                int hash_algo = hash_algo_by_ptr(transport_get_hash_algo(transport));
                repo_set_hash_algo(the_repository, hash_algo);
index 13cde5896aac80ab43408b1e89916832b1e8ec9c..6d62aaf59a030de6845dd1d27c9e9fa9421267b9 100644 (file)
@@ -2953,6 +2953,13 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                            pack_idx_opts.version);
                return 0;
        }
+       if (!strcmp(k, "pack.writereverseindex")) {
+               if (git_config_bool(k, v))
+                       pack_idx_opts.flags |= WRITE_REV;
+               else
+                       pack_idx_opts.flags &= ~WRITE_REV;
+               return 0;
+       }
        if (!strcmp(k, "uploadpack.blobpackfileuri")) {
                struct configured_exclusion *ex = xmalloc(sizeof(*ex));
                const char *oid_end, *pack_end;
@@ -3592,6 +3599,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 
        reset_pack_idx_option(&pack_idx_opts);
        git_config(git_pack_config, NULL);
+       if (git_env_bool(GIT_TEST_WRITE_REV_INDEX, 0))
+               pack_idx_opts.flags |= WRITE_REV;
 
        progress = isatty(2);
        argc = parse_options(argc, argv, prefix, pack_objects_options,
index 24c4162f7446ce89ff2fe9866bb0dc678785bed4..78bc9fa770624adc3d08857d1b5004e500ead2cb 100644 (file)
@@ -3,6 +3,7 @@
 #include "parse-options.h"
 #include "range-diff.h"
 #include "config.h"
+#include "revision.h"
 
 static const char * const builtin_range_diff_usage[] = {
 N_("git range-diff [<options>] <old-base>..<old-tip> <new-base>..<new-tip>"),
@@ -13,18 +14,27 @@ NULL
 
 int cmd_range_diff(int argc, const char **argv, const char *prefix)
 {
-       int creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT;
        struct diff_options diffopt = { NULL };
        struct strvec other_arg = STRVEC_INIT;
-       int simple_color = -1;
+       struct range_diff_options range_diff_opts = {
+               .creation_factor = RANGE_DIFF_CREATION_FACTOR_DEFAULT,
+               .diffopt = &diffopt,
+               .other_arg = &other_arg
+       };
+       int simple_color = -1, left_only = 0, right_only = 0;
        struct option range_diff_options[] = {
-               OPT_INTEGER(0, "creation-factor", &creation_factor,
+               OPT_INTEGER(0, "creation-factor",
+                           &range_diff_opts.creation_factor,
                            N_("Percentage by which creation is weighted")),
                OPT_BOOL(0, "no-dual-color", &simple_color,
                            N_("use simple diff colors")),
                OPT_PASSTHRU_ARGV(0, "notes", &other_arg,
                                  N_("notes"), N_("passed to 'git log'"),
                                  PARSE_OPT_OPTARG),
+               OPT_BOOL(0, "left-only", &left_only,
+                        N_("only emit output related to the first range")),
+               OPT_BOOL(0, "right-only", &right_only,
+                        N_("only emit output related to the second range")),
                OPT_END()
        };
        struct option *options;
@@ -46,12 +56,12 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
                diffopt.use_color = 1;
 
        if (argc == 2) {
-               if (!strstr(argv[0], ".."))
-                       die(_("no .. in range: '%s'"), argv[0]);
+               if (!is_range_diff_range(argv[0]))
+                       die(_("not a commit range: '%s'"), argv[0]);
                strbuf_addstr(&range1, argv[0]);
 
-               if (!strstr(argv[1], ".."))
-                       die(_("no .. in range: '%s'"), argv[1]);
+               if (!is_range_diff_range(argv[1]))
+                       die(_("not a commit range: '%s'"), argv[1]);
                strbuf_addstr(&range2, argv[1]);
        } else if (argc == 3) {
                strbuf_addf(&range1, "%s..%s", argv[0], argv[1]);
@@ -81,8 +91,10 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
        }
        FREE_AND_NULL(options);
 
-       res = show_range_diff(range1.buf, range2.buf, creation_factor,
-                             simple_color < 1, &diffopt, &other_arg);
+       range_diff_opts.dual_color = simple_color < 1;
+       range_diff_opts.left_only = left_only;
+       range_diff_opts.right_only = right_only;
+       res = show_range_diff(range1.buf, range2.buf, &range_diff_opts);
 
        strvec_clear(&other_arg);
        strbuf_release(&range1);
index d49d050e6e57c0dd8f795a36d6f1a64aa7c8ddee..b89ce31bf23104da3343c3b49c46b597bc117f66 100644 (file)
@@ -764,7 +764,7 @@ static void prepare_push_cert_sha1(struct child_process *proc)
 
                memset(&sigcheck, '\0', sizeof(sigcheck));
 
-               bogs = parse_signature(push_cert.buf, push_cert.len);
+               bogs = parse_signed_buffer(push_cert.buf, push_cert.len);
                check_signature(push_cert.buf, bogs, push_cert.buf + bogs,
                                push_cert.len - bogs, &sigcheck);
 
@@ -2050,7 +2050,7 @@ static void queue_commands_from_cert(struct command **tail,
                die("malformed push certificate %.*s", 100, push_cert->buf);
        else
                boc += 2;
-       eoc = push_cert->buf + parse_signature(push_cert->buf, push_cert->len);
+       eoc = push_cert->buf + parse_signed_buffer(push_cert->buf, push_cert->len);
 
        while (boc < eoc) {
                const char *eol = memchr(boc, '\n', eoc - boc);
index ca1d8079f320e6d6b93767110520dcdcd1fa0a97..09541d1c80483c1a20802ba34a747a1674811165 100644 (file)
@@ -602,6 +602,9 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
         */
        if (cb.cmd.stalefix) {
                repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
+               cb.cmd.revs.do_not_die_on_missing_tree = 1;
+               cb.cmd.revs.ignore_missing = 1;
+               cb.cmd.revs.ignore_missing_links = 1;
                if (flags & EXPIRE_REFLOGS_VERBOSE)
                        printf(_("Marking reachable objects..."));
                mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
index 2158b48f4cc72ed65c50c143dbedfc4c8a6fac32..01440de2d5a7030a10b391f1d7423b4cbaf2ed81 100644 (file)
@@ -209,6 +209,7 @@ static struct {
 } exts[] = {
        {".pack"},
        {".idx"},
+       {".rev", 1},
        {".bitmap", 1},
        {".promisor", 1},
 };
index 25c6c3b38d4b120687bb02220c3db25af2ae66de..b4d8ea0a35b5b2a0fde7302312b3bf39a5edcda1 100644 (file)
@@ -80,6 +80,19 @@ static int arg_show_object_names = 1;
 
 #define DEFAULT_OIDSET_SIZE     (16*1024)
 
+static int show_disk_usage;
+static off_t total_disk_usage;
+
+static off_t get_object_disk_usage(struct object *obj)
+{
+       off_t size;
+       struct object_info oi = OBJECT_INFO_INIT;
+       oi.disk_sizep = &size;
+       if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
+               die(_("unable to get disk usage of %s"), oid_to_hex(&obj->oid));
+       return size;
+}
+
 static void finish_commit(struct commit *commit);
 static void show_commit(struct commit *commit, void *data)
 {
@@ -88,6 +101,9 @@ static void show_commit(struct commit *commit, void *data)
 
        display_progress(progress, ++progress_counter);
 
+       if (show_disk_usage)
+               total_disk_usage += get_object_disk_usage(&commit->object);
+
        if (info->flags & REV_LIST_QUIET) {
                finish_commit(commit);
                return;
@@ -258,6 +274,8 @@ static void show_object(struct object *obj, const char *name, void *cb_data)
        if (finish_object(obj, name, cb_data))
                return;
        display_progress(progress, ++progress_counter);
+       if (show_disk_usage)
+               total_disk_usage += get_object_disk_usage(obj);
        if (info->flags & REV_LIST_QUIET)
                return;
 
@@ -452,6 +470,23 @@ static int try_bitmap_traversal(struct rev_info *revs,
        return 0;
 }
 
+static int try_bitmap_disk_usage(struct rev_info *revs,
+                                struct list_objects_filter_options *filter)
+{
+       struct bitmap_index *bitmap_git;
+
+       if (!show_disk_usage)
+               return -1;
+
+       bitmap_git = prepare_bitmap_walk(revs, filter);
+       if (!bitmap_git)
+               return -1;
+
+       printf("%"PRIuMAX"\n",
+              (uintmax_t)get_disk_usage_from_bitmap(bitmap_git, revs));
+       return 0;
+}
+
 int cmd_rev_list(int argc, const char **argv, const char *prefix)
 {
        struct rev_info revs;
@@ -584,6 +619,12 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        continue;
                }
 
+               if (!strcmp(arg, "--disk-usage")) {
+                       show_disk_usage = 1;
+                       info.flags |= REV_LIST_QUIET;
+                       continue;
+               }
+
                usage(rev_list_usage);
 
        }
@@ -626,6 +667,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (use_bitmap_index) {
                if (!try_bitmap_count(&revs, &filter_options))
                        return 0;
+               if (!try_bitmap_disk_usage(&revs, &filter_options))
+                       return 0;
                if (!try_bitmap_traversal(&revs, &filter_options))
                        return 0;
        }
@@ -690,5 +733,8 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        printf("%d\n", revs.count_left + revs.count_right);
        }
 
+       if (show_disk_usage)
+               printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
+
        return 0;
 }
index 9bc85f91cd004024b5bba9bf9cb587101e9dedce..6f2b58f6ab2046aebd13a1a6486f1bd59662f7f9 100644 (file)
@@ -87,7 +87,7 @@ static const char * const git_stash_save_usage[] = {
        NULL
 };
 
-static const char *ref_stash = "refs/stash";
+static const char ref_stash[] = "refs/stash";
 static struct strbuf stash_index_path = STRBUF_INIT;
 
 /*
index c2bd882d17e72b3950b4d45040352c253c3c78e1..9d505a6329c8e2a61a91f27c84ee24f668bc688e 100644 (file)
@@ -1257,7 +1257,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
        git_config(git_diff_basic_config, NULL);
        init_revisions(&rev, info->prefix);
        rev.abbrev = 0;
-       precompose_argv(diff_args.nr, diff_args.v);
+       precompose_argv_prefix(diff_args.nr, diff_args.v, NULL);
        setup_revisions(diff_args.nr, diff_args.v, &rev, NULL);
        rev.diffopt.output_format = DIFF_FORMAT_NO_OUTPUT | DIFF_FORMAT_CALLBACK;
        rev.diffopt.format_callback = submodule_summary_callback;
index e8b85eefd878b5b2fd1eafe0627d811ecd49b8ea..4237dc724c75f3ae30df6716ae37dd37b844bbda 100644 (file)
@@ -198,11 +198,17 @@ static void write_tag_body(int fd, const struct object_id *oid)
 {
        unsigned long size;
        enum object_type type;
-       char *buf, *sp;
+       char *buf, *sp, *orig;
+       struct strbuf payload = STRBUF_INIT;
+       struct strbuf signature = STRBUF_INIT;
 
-       buf = read_object_file(oid, &type, &size);
+       orig = buf = read_object_file(oid, &type, &size);
        if (!buf)
                return;
+       if (parse_signature(buf, size, &payload, &signature)) {
+               buf = payload.buf;
+               size = payload.len;
+       }
        /* skip header */
        sp = strstr(buf, "\n\n");
 
@@ -211,9 +217,11 @@ static void write_tag_body(int fd, const struct object_id *oid)
                return;
        }
        sp += 2; /* skip the 2 LFs */
-       write_or_die(fd, sp, parse_signature(sp, buf + size - sp));
+       write_or_die(fd, sp, buf + size - sp);
 
-       free(buf);
+       free(orig);
+       strbuf_release(&payload);
+       strbuf_release(&signature);
 }
 
 static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
index 50e0b90073f13307f902286dd3bad560363d05f1..a66b5e8c75aec2e4a9ea80227a8b492c8efcdf8d 100755 (executable)
@@ -24,6 +24,7 @@ linux-gcc)
        export GIT_TEST_MULTI_PACK_INDEX=1
        export GIT_TEST_ADD_I_USE_BUILTIN=1
        export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
+       export GIT_TEST_WRITE_REV_INDEX=1
        make test
        ;;
 linux-clang)
index 65410602714e642922bc5f6060b4b085db8464a5..ed31843fa522ee5a49e151c41e1527ebd9c6dbc0 100644 (file)
@@ -38,11 +38,13 @@ void git_test_write_commit_graph_or_die(void)
 #define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
 #define GRAPH_CHUNKID_OIDLOOKUP 0x4f49444c /* "OIDL" */
 #define GRAPH_CHUNKID_DATA 0x43444154 /* "CDAT" */
+#define GRAPH_CHUNKID_GENERATION_DATA 0x47444154 /* "GDAT" */
+#define GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW 0x47444f56 /* "GDOV" */
 #define GRAPH_CHUNKID_EXTRAEDGES 0x45444745 /* "EDGE" */
 #define GRAPH_CHUNKID_BLOOMINDEXES 0x42494458 /* "BIDX" */
 #define GRAPH_CHUNKID_BLOOMDATA 0x42444154 /* "BDAT" */
 #define GRAPH_CHUNKID_BASE 0x42415345 /* "BASE" */
-#define MAX_NUM_CHUNKS 7
+#define MAX_NUM_CHUNKS 9
 
 #define GRAPH_DATA_WIDTH (the_hash_algo->rawsz + 16)
 
@@ -61,9 +63,13 @@ void git_test_write_commit_graph_or_die(void)
 #define GRAPH_MIN_SIZE (GRAPH_HEADER_SIZE + 4 * GRAPH_CHUNKLOOKUP_WIDTH \
                        + GRAPH_FANOUT_SIZE + the_hash_algo->rawsz)
 
+#define CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW (1ULL << 31)
+
 /* Remember to update object flag allocation in object.h */
 #define REACHABLE       (1u<<15)
 
+define_commit_slab(topo_level_slab, uint32_t);
+
 /* Keep track of the order in which commits are added to our list. */
 define_commit_slab(commit_pos, int);
 static struct commit_pos commit_pos = COMMIT_SLAB_INIT(1, commit_pos);
@@ -99,7 +105,7 @@ uint32_t commit_graph_position(const struct commit *c)
        return data ? data->graph_pos : COMMIT_NOT_FROM_GRAPH;
 }
 
-uint32_t commit_graph_generation(const struct commit *c)
+timestamp_t commit_graph_generation(const struct commit *c)
 {
        struct commit_graph_data *data =
                commit_graph_data_slab_peek(&commit_graph_data_slab, c);
@@ -139,13 +145,17 @@ static struct commit_graph_data *commit_graph_data_at(const struct commit *c)
        return data;
 }
 
+/*
+ * Should be used only while writing commit-graph as it compares
+ * generation value of commits by directly accessing commit-slab.
+ */
 static int commit_gen_cmp(const void *va, const void *vb)
 {
        const struct commit *a = *(const struct commit **)va;
        const struct commit *b = *(const struct commit **)vb;
 
-       uint32_t generation_a = commit_graph_generation(a);
-       uint32_t generation_b = commit_graph_generation(b);
+       const timestamp_t generation_a = commit_graph_data_at(a)->generation;
+       const timestamp_t generation_b = commit_graph_data_at(b)->generation;
        /* lower generation commits first */
        if (generation_a < generation_b)
                return -1;
@@ -205,16 +215,24 @@ static int commit_graph_compatible(struct repository *r)
 
        if (read_replace_refs) {
                prepare_replace_object(r);
-               if (hashmap_get_size(&r->objects->replace_map->map))
+               if (hashmap_get_size(&r->objects->replace_map->map)) {
+                       warning(_("repository contains replace objects; "
+                              "skipping commit-graph"));
                        return 0;
+               }
        }
 
        prepare_commit_graft(r);
        if (r->parsed_objects &&
-           (r->parsed_objects->grafts_nr || r->parsed_objects->substituted_parent))
+           (r->parsed_objects->grafts_nr || r->parsed_objects->substituted_parent)) {
+               warning(_("repository contains (deprecated) grafts; "
+                      "skipping commit-graph"));
                return 0;
-       if (is_repository_shallow(r))
+       }
+       if (is_repository_shallow(r)) {
+               warning(_("repository is shallow; skipping commit-graph"));
                return 0;
+       }
 
        return 1;
 }
@@ -388,6 +406,20 @@ struct commit_graph *parse_commit_graph(struct repository *r,
                                graph->chunk_commit_data = data + chunk_offset;
                        break;
 
+               case GRAPH_CHUNKID_GENERATION_DATA:
+                       if (graph->chunk_generation_data)
+                               chunk_repeated = 1;
+                       else
+                               graph->chunk_generation_data = data + chunk_offset;
+                       break;
+
+               case GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW:
+                       if (graph->chunk_generation_data_overflow)
+                               chunk_repeated = 1;
+                       else
+                               graph->chunk_generation_data_overflow = data + chunk_offset;
+                       break;
+
                case GRAPH_CHUNKID_EXTRAEDGES:
                        if (graph->chunk_extra_edges)
                                chunk_repeated = 1;
@@ -590,6 +622,31 @@ static struct commit_graph *load_commit_graph_chain(struct repository *r,
        return graph_chain;
 }
 
+/*
+ * returns 1 if and only if all graphs in the chain have
+ * corrected commit dates stored in the generation_data chunk.
+ */
+static int validate_mixed_generation_chain(struct commit_graph *g)
+{
+       int read_generation_data = 1;
+       struct commit_graph *p = g;
+
+       while (read_generation_data && p) {
+               read_generation_data = p->read_generation_data;
+               p = p->base_graph;
+       }
+
+       if (read_generation_data)
+               return 1;
+
+       while (g) {
+               g->read_generation_data = 0;
+               g = g->base_graph;
+       }
+
+       return 0;
+}
+
 struct commit_graph *read_commit_graph_one(struct repository *r,
                                           struct object_directory *odb)
 {
@@ -598,6 +655,8 @@ struct commit_graph *read_commit_graph_one(struct repository *r,
        if (!g)
                g = load_commit_graph_chain(r, odb);
 
+       validate_mixed_generation_chain(g);
+
        return g;
 }
 
@@ -673,6 +732,20 @@ int generation_numbers_enabled(struct repository *r)
        return !!first_generation;
 }
 
+int corrected_commit_dates_enabled(struct repository *r)
+{
+       struct commit_graph *g;
+       if (!prepare_commit_graph(r))
+               return 0;
+
+       g = r->objects->commit_graph;
+
+       if (!g->num_commits)
+               return 0;
+
+       return g->read_generation_data;
+}
+
 struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r)
 {
        struct commit_graph *g = r->objects->commit_graph;
@@ -748,17 +821,41 @@ static void fill_commit_graph_info(struct commit *item, struct commit_graph *g,
 {
        const unsigned char *commit_data;
        struct commit_graph_data *graph_data;
-       uint32_t lex_index;
+       uint32_t lex_index, offset_pos;
+       uint64_t date_high, date_low, offset;
 
        while (pos < g->num_commits_in_base)
                g = g->base_graph;
 
+       if (pos >= g->num_commits + g->num_commits_in_base)
+               die(_("invalid commit position. commit-graph is likely corrupt"));
+
        lex_index = pos - g->num_commits_in_base;
        commit_data = g->chunk_commit_data + GRAPH_DATA_WIDTH * lex_index;
 
        graph_data = commit_graph_data_at(item);
        graph_data->graph_pos = pos;
-       graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+
+       date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
+       date_low = get_be32(commit_data + g->hash_len + 12);
+       item->date = (timestamp_t)((date_high << 32) | date_low);
+
+       if (g->read_generation_data) {
+               offset = (timestamp_t)get_be32(g->chunk_generation_data + sizeof(uint32_t) * lex_index);
+
+               if (offset & CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW) {
+                       if (!g->chunk_generation_data_overflow)
+                               die(_("commit-graph requires overflow generation data but has none"));
+
+                       offset_pos = offset ^ CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW;
+                       graph_data->generation = get_be64(g->chunk_generation_data_overflow + 8 * offset_pos);
+               } else
+                       graph_data->generation = item->date + offset;
+       } else
+               graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
+
+       if (g->topo_levels)
+               *topo_level_slab_at(g->topo_levels, item) = get_be32(commit_data + g->hash_len + 8) >> 2;
 }
 
 static inline void set_commit_tree(struct commit *c, struct tree *t)
@@ -772,38 +869,22 @@ static int fill_commit_in_graph(struct repository *r,
 {
        uint32_t edge_value;
        uint32_t *parent_data_ptr;
-       uint64_t date_low, date_high;
        struct commit_list **pptr;
-       struct commit_graph_data *graph_data;
        const unsigned char *commit_data;
        uint32_t lex_index;
 
        while (pos < g->num_commits_in_base)
                g = g->base_graph;
 
-       if (pos >= g->num_commits + g->num_commits_in_base)
-               die(_("invalid commit position. commit-graph is likely corrupt"));
+       fill_commit_graph_info(item, g, pos);
 
-       /*
-        * Store the "full" position, but then use the
-        * "local" position for the rest of the calculation.
-        */
-       graph_data = commit_graph_data_at(item);
-       graph_data->graph_pos = pos;
        lex_index = pos - g->num_commits_in_base;
-
        commit_data = g->chunk_commit_data + (g->hash_len + 16) * lex_index;
 
        item->object.parsed = 1;
 
        set_commit_tree(item, NULL);
 
-       date_high = get_be32(commit_data + g->hash_len + 8) & 0x3;
-       date_low = get_be32(commit_data + g->hash_len + 12);
-       item->date = (timestamp_t)((date_high << 32) | date_low);
-
-       graph_data->generation = get_be32(commit_data + g->hash_len + 8) >> 2;
-
        pptr = &item->parents;
 
        edge_value = get_be32(commit_data + g->hash_len);
@@ -943,6 +1024,7 @@ struct write_commit_graph_context {
        struct oid_array oids;
        struct packed_commit_list commits;
        int num_extra_edges;
+       int num_generation_data_overflows;
        unsigned long approx_nr_objects;
        struct progress *progress;
        int progress_done;
@@ -961,8 +1043,11 @@ struct write_commit_graph_context {
                 report_progress:1,
                 split:1,
                 changed_paths:1,
-                order_by_pack:1;
+                order_by_pack:1,
+                write_generation_data:1,
+                trust_generation_numbers:1;
 
+       struct topo_level_slab *topo_levels;
        const struct commit_graph_opts *opts;
        size_t total_bloom_filter_data_size;
        const struct bloom_filter_settings *bloom_settings;
@@ -1032,7 +1117,7 @@ static int write_graph_chunk_data(struct hashfile *f,
                uint32_t packedDate[2];
                display_progress(ctx->progress, ++ctx->progress_cnt);
 
-               if (parse_commit_no_graph(*list))
+               if (repo_parse_commit_no_graph(ctx->r, *list))
                        die(_("unable to parse commit %s"),
                                oid_to_hex(&(*list)->object.oid));
                tree = get_commit_tree_oid(*list);
@@ -1109,7 +1194,7 @@ static int write_graph_chunk_data(struct hashfile *f,
                else
                        packedDate[0] = 0;
 
-               packedDate[0] |= htonl(commit_graph_data_at(*list)->generation << 2);
+               packedDate[0] |= htonl(*topo_level_slab_at(ctx->topo_levels, *list) << 2);
 
                packedDate[1] = htonl((*list)->date);
                hashwrite(f, packedDate, 8);
@@ -1120,6 +1205,47 @@ static int write_graph_chunk_data(struct hashfile *f,
        return 0;
 }
 
+static int write_graph_chunk_generation_data(struct hashfile *f,
+                                             struct write_commit_graph_context *ctx)
+{
+       int i, num_generation_data_overflows = 0;
+
+       for (i = 0; i < ctx->commits.nr; i++) {
+               struct commit *c = ctx->commits.list[i];
+               timestamp_t offset;
+               repo_parse_commit(ctx->r, c);
+               offset = commit_graph_data_at(c)->generation - c->date;
+               display_progress(ctx->progress, ++ctx->progress_cnt);
+
+               if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
+                       offset = CORRECTED_COMMIT_DATE_OFFSET_OVERFLOW | num_generation_data_overflows;
+                       num_generation_data_overflows++;
+               }
+
+               hashwrite_be32(f, offset);
+       }
+
+       return 0;
+}
+
+static int write_graph_chunk_generation_data_overflow(struct hashfile *f,
+                                                      struct write_commit_graph_context *ctx)
+{
+       int i;
+       for (i = 0; i < ctx->commits.nr; i++) {
+               struct commit *c = ctx->commits.list[i];
+               timestamp_t offset = commit_graph_data_at(c)->generation - c->date;
+               display_progress(ctx->progress, ++ctx->progress_cnt);
+
+               if (offset > GENERATION_NUMBER_V2_OFFSET_MAX) {
+                       hashwrite_be32(f, offset >> 32);
+                       hashwrite_be32(f, (uint32_t) offset);
+               }
+       }
+
+       return 0;
+}
+
 static int write_graph_chunk_extra_edges(struct hashfile *f,
                                         struct write_commit_graph_context *ctx)
 {
@@ -1306,11 +1432,11 @@ static void close_reachable(struct write_commit_graph_context *ctx)
                if (!commit)
                        continue;
                if (ctx->split) {
-                       if ((!parse_commit(commit) &&
+                       if ((!repo_parse_commit(ctx->r, commit) &&
                             commit_graph_position(commit) == COMMIT_NOT_FROM_GRAPH) ||
                            flags == COMMIT_GRAPH_SPLIT_REPLACE)
                                add_missing_parents(ctx, commit);
-               } else if (!parse_commit_no_graph(commit))
+               } else if (!repo_parse_commit_no_graph(ctx->r, commit))
                        add_missing_parents(ctx, commit);
        }
        stop_progress(&ctx->progress);
@@ -1329,6 +1455,59 @@ static void close_reachable(struct write_commit_graph_context *ctx)
        stop_progress(&ctx->progress);
 }
 
+static void compute_topological_levels(struct write_commit_graph_context *ctx)
+{
+       int i;
+       struct commit_list *list = NULL;
+
+       if (ctx->report_progress)
+               ctx->progress = start_delayed_progress(
+                                       _("Computing commit graph topological levels"),
+                                       ctx->commits.nr);
+       for (i = 0; i < ctx->commits.nr; i++) {
+               struct commit *c = ctx->commits.list[i];
+               uint32_t level;
+
+               repo_parse_commit(ctx->r, c);
+               level = *topo_level_slab_at(ctx->topo_levels, c);
+
+               display_progress(ctx->progress, i + 1);
+               if (level != GENERATION_NUMBER_ZERO)
+                       continue;
+
+               commit_list_insert(c, &list);
+               while (list) {
+                       struct commit *current = list->item;
+                       struct commit_list *parent;
+                       int all_parents_computed = 1;
+                       uint32_t max_level = 0;
+
+                       for (parent = current->parents; parent; parent = parent->next) {
+                               repo_parse_commit(ctx->r, parent->item);
+                               level = *topo_level_slab_at(ctx->topo_levels, parent->item);
+
+                               if (level == GENERATION_NUMBER_ZERO) {
+                                       all_parents_computed = 0;
+                                       commit_list_insert(parent->item, &list);
+                                       break;
+                               }
+
+                               if (level > max_level)
+                                       max_level = level;
+                       }
+
+                       if (all_parents_computed) {
+                               pop_commit(&list);
+
+                               if (max_level > GENERATION_NUMBER_V1_MAX - 1)
+                                       max_level = GENERATION_NUMBER_V1_MAX - 1;
+                               *topo_level_slab_at(ctx->topo_levels, current) = max_level + 1;
+                       }
+               }
+       }
+       stop_progress(&ctx->progress);
+}
+
 static void compute_generation_numbers(struct write_commit_graph_context *ctx)
 {
        int i;
@@ -1338,42 +1517,56 @@ static void compute_generation_numbers(struct write_commit_graph_context *ctx)
                ctx->progress = start_delayed_progress(
                                        _("Computing commit graph generation numbers"),
                                        ctx->commits.nr);
+
+       if (!ctx->trust_generation_numbers) {
+               for (i = 0; i < ctx->commits.nr; i++) {
+                       struct commit *c = ctx->commits.list[i];
+                       repo_parse_commit(ctx->r, c);
+                       commit_graph_data_at(c)->generation = GENERATION_NUMBER_ZERO;
+               }
+       }
+
        for (i = 0; i < ctx->commits.nr; i++) {
-               uint32_t generation = commit_graph_data_at(ctx->commits.list[i])->generation;
+               struct commit *c = ctx->commits.list[i];
+               timestamp_t corrected_commit_date;
+
+               repo_parse_commit(ctx->r, c);
+               corrected_commit_date = commit_graph_data_at(c)->generation;
 
                display_progress(ctx->progress, i + 1);
-               if (generation != GENERATION_NUMBER_INFINITY &&
-                   generation != GENERATION_NUMBER_ZERO)
+               if (corrected_commit_date != GENERATION_NUMBER_ZERO)
                        continue;
 
-               commit_list_insert(ctx->commits.list[i], &list);
+               commit_list_insert(c, &list);
                while (list) {
                        struct commit *current = list->item;
                        struct commit_list *parent;
                        int all_parents_computed = 1;
-                       uint32_t max_generation = 0;
+                       timestamp_t max_corrected_commit_date = 0;
 
                        for (parent = current->parents; parent; parent = parent->next) {
-                               generation = commit_graph_data_at(parent->item)->generation;
+                               repo_parse_commit(ctx->r, parent->item);
+                               corrected_commit_date = commit_graph_data_at(parent->item)->generation;
 
-                               if (generation == GENERATION_NUMBER_INFINITY ||
-                                   generation == GENERATION_NUMBER_ZERO) {
+                               if (corrected_commit_date == GENERATION_NUMBER_ZERO) {
                                        all_parents_computed = 0;
                                        commit_list_insert(parent->item, &list);
                                        break;
-                               } else if (generation > max_generation) {
-                                       max_generation = generation;
                                }
+
+                               if (corrected_commit_date > max_corrected_commit_date)
+                                       max_corrected_commit_date = corrected_commit_date;
                        }
 
                        if (all_parents_computed) {
-                               struct commit_graph_data *data = commit_graph_data_at(current);
-
-                               data->generation = max_generation + 1;
                                pop_commit(&list);
 
-                               if (data->generation > GENERATION_NUMBER_MAX)
-                                       data->generation = GENERATION_NUMBER_MAX;
+                               if (current->date && current->date > max_corrected_commit_date)
+                                       max_corrected_commit_date = current->date - 1;
+                               commit_graph_data_at(current)->generation = max_corrected_commit_date + 1;
+
+                               if (commit_graph_data_at(current)->generation - current->date > GENERATION_NUMBER_V2_OFFSET_MAX)
+                                       ctx->num_generation_data_overflows++;
                        }
                }
        }
@@ -1593,9 +1786,9 @@ static void copy_oids_to_commits(struct write_commit_graph_context *ctx)
                        continue;
 
                if (ctx->split && flags == COMMIT_GRAPH_SPLIT_REPLACE)
-                       parse_commit(ctx->commits.list[ctx->commits.nr]);
+                       repo_parse_commit(ctx->r, ctx->commits.list[ctx->commits.nr]);
                else
-                       parse_commit_no_graph(ctx->commits.list[ctx->commits.nr]);
+                       repo_parse_commit_no_graph(ctx->r, ctx->commits.list[ctx->commits.nr]);
 
                num_parents = commit_list_count(ctx->commits.list[ctx->commits.nr]->parents);
                if (num_parents > 2)
@@ -1707,6 +1900,21 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)
        chunks[2].id = GRAPH_CHUNKID_DATA;
        chunks[2].size = (hashsz + 16) * ctx->commits.nr;
        chunks[2].write_fn = write_graph_chunk_data;
+
+       if (git_env_bool(GIT_TEST_COMMIT_GRAPH_NO_GDAT, 0))
+               ctx->write_generation_data = 0;
+       if (ctx->write_generation_data) {
+               chunks[num_chunks].id = GRAPH_CHUNKID_GENERATION_DATA;
+               chunks[num_chunks].size = sizeof(uint32_t) * ctx->commits.nr;
+               chunks[num_chunks].write_fn = write_graph_chunk_generation_data;
+               num_chunks++;
+       }
+       if (ctx->num_generation_data_overflows) {
+               chunks[num_chunks].id = GRAPH_CHUNKID_GENERATION_DATA_OVERFLOW;
+               chunks[num_chunks].size = sizeof(timestamp_t) * ctx->num_generation_data_overflows;
+               chunks[num_chunks].write_fn = write_graph_chunk_generation_data_overflow;
+               num_chunks++;
+       }
        if (ctx->num_extra_edges) {
                chunks[num_chunks].id = GRAPH_CHUNKID_EXTRAEDGES;
                chunks[num_chunks].size = 4 * ctx->num_extra_edges;
@@ -1918,6 +2126,13 @@ static void split_graph_merge_strategy(struct write_commit_graph_context *ctx)
                if (i < ctx->num_commit_graphs_after)
                        ctx->commit_graph_hash_after[i] = xstrdup(oid_to_hex(&g->oid));
 
+               /*
+                * If the topmost remaining layer has generation data chunk, the
+                * resultant layer also has generation data chunk.
+                */
+               if (i == ctx->num_commit_graphs_after - 2)
+                       ctx->write_generation_data = !!g->chunk_generation_data;
+
                i--;
                g = g->base_graph;
        }
@@ -2109,6 +2324,7 @@ int write_commit_graph(struct object_directory *odb,
        int res = 0;
        int replace = 0;
        struct bloom_filter_settings bloom_settings = DEFAULT_BLOOM_FILTER_SETTINGS;
+       struct topo_level_slab topo_levels;
 
        prepare_repo_settings(the_repository);
        if (!the_repository->settings.core_commit_graph) {
@@ -2126,6 +2342,8 @@ int write_commit_graph(struct object_directory *odb,
        ctx->split = flags & COMMIT_GRAPH_WRITE_SPLIT ? 1 : 0;
        ctx->opts = opts;
        ctx->total_bloom_filter_data_size = 0;
+       ctx->write_generation_data = 1;
+       ctx->num_generation_data_overflows = 0;
 
        bloom_settings.bits_per_entry = git_env_ulong("GIT_TEST_BLOOM_SETTINGS_BITS_PER_ENTRY",
                                                      bloom_settings.bits_per_entry);
@@ -2135,11 +2353,23 @@ int write_commit_graph(struct object_directory *odb,
                                                         bloom_settings.max_changed_paths);
        ctx->bloom_settings = &bloom_settings;
 
+       init_topo_level_slab(&topo_levels);
+       ctx->topo_levels = &topo_levels;
+
+       prepare_commit_graph(ctx->r);
+       if (ctx->r->objects->commit_graph) {
+               struct commit_graph *g = ctx->r->objects->commit_graph;
+
+               while (g) {
+                       g->topo_levels = &topo_levels;
+                       g = g->base_graph;
+               }
+       }
+
        if (flags & COMMIT_GRAPH_WRITE_BLOOM_FILTERS)
                ctx->changed_paths = 1;
        if (!(flags & COMMIT_GRAPH_NO_WRITE_BLOOM_FILTERS)) {
                struct commit_graph *g;
-               prepare_commit_graph_one(ctx->r, ctx->odb);
 
                g = ctx->r->objects->commit_graph;
 
@@ -2151,10 +2381,7 @@ int write_commit_graph(struct object_directory *odb,
        }
 
        if (ctx->split) {
-               struct commit_graph *g;
-               prepare_commit_graph(ctx->r);
-
-               g = ctx->r->objects->commit_graph;
+               struct commit_graph *g = ctx->r->objects->commit_graph;
 
                while (g) {
                        ctx->num_commit_graphs_before++;
@@ -2178,9 +2405,6 @@ int write_commit_graph(struct object_directory *odb,
 
        ctx->approx_nr_objects = approximate_object_count();
 
-       if (ctx->append)
-               prepare_commit_graph_one(ctx->r, ctx->odb);
-
        if (ctx->append && ctx->r->objects->commit_graph) {
                struct commit_graph *g = ctx->r->objects->commit_graph;
                for (i = 0; i < g->num_commits; i++) {
@@ -2227,7 +2451,11 @@ int write_commit_graph(struct object_directory *odb,
        } else
                ctx->num_commit_graphs_after = 1;
 
-       compute_generation_numbers(ctx);
+       ctx->trust_generation_numbers = validate_mixed_generation_chain(ctx->r->objects->commit_graph);
+
+       compute_topological_levels(ctx);
+       if (ctx->write_generation_data)
+               compute_generation_numbers(ctx);
 
        if (ctx->changed_paths)
                compute_bloom_filters(ctx);
@@ -2355,8 +2583,8 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
        for (i = 0; i < g->num_commits; i++) {
                struct commit *graph_commit, *odb_commit;
                struct commit_list *graph_parents, *odb_parents;
-               uint32_t max_generation = 0;
-               uint32_t generation;
+               timestamp_t max_generation = 0;
+               timestamp_t generation;
 
                display_progress(progress, i + 1);
                hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
@@ -2420,16 +2648,17 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)
                        continue;
 
                /*
-                * If one of our parents has generation GENERATION_NUMBER_MAX, then
-                * our generation is also GENERATION_NUMBER_MAX. Decrement to avoid
-                * extra logic in the following condition.
+                * If we are using topological level and one of our parents has
+                * generation GENERATION_NUMBER_V1_MAX, then our generation is
+                * also GENERATION_NUMBER_V1_MAX. Decrement to avoid extra logic
+                * in the following condition.
                 */
-               if (max_generation == GENERATION_NUMBER_MAX)
+               if (!g->read_generation_data && max_generation == GENERATION_NUMBER_V1_MAX)
                        max_generation--;
 
                generation = commit_graph_generation(graph_commit);
-               if (generation != max_generation + 1)
-                       graph_report(_("commit-graph generation for commit %s is %u != %u"),
+               if (generation < max_generation + 1)
+                       graph_report(_("commit-graph generation for commit %s is %"PRItime" < %"PRItime),
                                     oid_to_hex(&cur_oid),
                                     generation,
                                     max_generation + 1);
index f8e92500c6e4bc004c9945b29d27da89319d0bd0..97f3497c2790c5bca0f9c73dd069291c95456895 100644 (file)
@@ -6,6 +6,7 @@
 #include "oidset.h"
 
 #define GIT_TEST_COMMIT_GRAPH "GIT_TEST_COMMIT_GRAPH"
+#define GIT_TEST_COMMIT_GRAPH_NO_GDAT "GIT_TEST_COMMIT_GRAPH_NO_GDAT"
 #define GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE "GIT_TEST_COMMIT_GRAPH_DIE_ON_PARSE"
 #define GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS "GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS"
 
@@ -63,16 +64,20 @@ struct commit_graph {
        struct object_directory *odb;
 
        uint32_t num_commits_in_base;
+       unsigned int read_generation_data;
        struct commit_graph *base_graph;
 
        const uint32_t *chunk_oid_fanout;
        const unsigned char *chunk_oid_lookup;
        const unsigned char *chunk_commit_data;
+       const unsigned char *chunk_generation_data;
+       const unsigned char *chunk_generation_data_overflow;
        const unsigned char *chunk_extra_edges;
        const unsigned char *chunk_base_graphs;
        const unsigned char *chunk_bloom_indexes;
        const unsigned char *chunk_bloom_data;
 
+       struct topo_level_slab *topo_levels;
        struct bloom_filter_settings *bloom_filter_settings;
 };
 
@@ -90,6 +95,12 @@ struct commit_graph *parse_commit_graph(struct repository *r,
  */
 int generation_numbers_enabled(struct repository *r);
 
+/*
+ * Return 1 if and only if the repository has a commit-graph
+ * file and generation data chunk has been written for the file.
+ */
+int corrected_commit_dates_enabled(struct repository *r);
+
 struct bloom_filter_settings *get_bloom_filter_settings(struct repository *r);
 
 enum commit_graph_write_flags {
@@ -144,12 +155,12 @@ void disable_commit_graph(struct repository *r);
 
 struct commit_graph_data {
        uint32_t graph_pos;
-       uint32_t generation;
+       timestamp_t generation;
 };
 
 /*
  * Commits should be parsed before accessing generation, graph positions.
  */
-uint32_t commit_graph_generation(const struct commit *);
+timestamp_t commit_graph_generation(const struct commit *);
 uint32_t commit_graph_position(const struct commit *);
 #endif
index 50175b159e709ef75e01e7c8380fef127d83d408..e38771ca5a1f63c84b7ad3e6d3bf6013c923042a 100644 (file)
@@ -32,14 +32,14 @@ static int queue_has_nonstale(struct prio_queue *queue)
 static struct commit_list *paint_down_to_common(struct repository *r,
                                                struct commit *one, int n,
                                                struct commit **twos,
-                                               int min_generation)
+                                               timestamp_t min_generation)
 {
        struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
        struct commit_list *result = NULL;
        int i;
-       uint32_t last_gen = GENERATION_NUMBER_INFINITY;
+       timestamp_t last_gen = GENERATION_NUMBER_INFINITY;
 
-       if (!min_generation)
+       if (!min_generation && !corrected_commit_dates_enabled(r))
                queue.compare = compare_commits_by_commit_date;
 
        one->object.flags |= PARENT1;
@@ -58,10 +58,10 @@ static struct commit_list *paint_down_to_common(struct repository *r,
                struct commit *commit = prio_queue_get(&queue);
                struct commit_list *parents;
                int flags;
-               uint32_t generation = commit_graph_generation(commit);
+               timestamp_t generation = commit_graph_generation(commit);
 
                if (min_generation && generation > last_gen)
-                       BUG("bad generation skip %8x > %8x at %s",
+                       BUG("bad generation skip %"PRItime" > %"PRItime" at %s",
                            generation, last_gen,
                            oid_to_hex(&commit->object.oid));
                last_gen = generation;
@@ -177,12 +177,12 @@ static int remove_redundant(struct repository *r, struct commit **array, int cnt
                repo_parse_commit(r, array[i]);
        for (i = 0; i < cnt; i++) {
                struct commit_list *common;
-               uint32_t min_generation = commit_graph_generation(array[i]);
+               timestamp_t min_generation = commit_graph_generation(array[i]);
 
                if (redundant[i])
                        continue;
                for (j = filled = 0; j < cnt; j++) {
-                       uint32_t curr_generation;
+                       timestamp_t curr_generation;
                        if (i == j || redundant[j])
                                continue;
                        filled_index[filled] = j;
@@ -321,7 +321,7 @@ int repo_in_merge_bases_many(struct repository *r, struct commit *commit,
 {
        struct commit_list *bases;
        int ret = 0, i;
-       uint32_t generation, max_generation = GENERATION_NUMBER_ZERO;
+       timestamp_t generation, max_generation = GENERATION_NUMBER_ZERO;
 
        if (repo_parse_commit(r, commit))
                return ret;
@@ -470,7 +470,7 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
 static enum contains_result contains_test(struct commit *candidate,
                                          const struct commit_list *want,
                                          struct contains_cache *cache,
-                                         uint32_t cutoff)
+                                         timestamp_t cutoff)
 {
        enum contains_result *cached = contains_cache_at(cache, candidate);
 
@@ -506,11 +506,11 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
 {
        struct contains_stack contains_stack = { 0, 0, NULL };
        enum contains_result result;
-       uint32_t cutoff = GENERATION_NUMBER_INFINITY;
+       timestamp_t cutoff = GENERATION_NUMBER_INFINITY;
        const struct commit_list *p;
 
        for (p = want; p; p = p->next) {
-               uint32_t generation;
+               timestamp_t generation;
                struct commit *c = p->item;
                load_commit_graph_info(the_repository, c);
                generation = commit_graph_generation(c);
@@ -566,8 +566,8 @@ static int compare_commits_by_gen(const void *_a, const void *_b)
        const struct commit *a = *(const struct commit * const *)_a;
        const struct commit *b = *(const struct commit * const *)_b;
 
-       uint32_t generation_a = commit_graph_generation(a);
-       uint32_t generation_b = commit_graph_generation(b);
+       timestamp_t generation_a = commit_graph_generation(a);
+       timestamp_t generation_b = commit_graph_generation(b);
 
        if (generation_a < generation_b)
                return -1;
@@ -580,7 +580,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
                                 unsigned int with_flag,
                                 unsigned int assign_flag,
                                 time_t min_commit_date,
-                                uint32_t min_generation)
+                                timestamp_t min_generation)
 {
        struct commit **list = NULL;
        int i;
@@ -681,13 +681,13 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
        time_t min_commit_date = cutoff_by_min_date ? from->item->date : 0;
        struct commit_list *from_iter = from, *to_iter = to;
        int result;
-       uint32_t min_generation = GENERATION_NUMBER_INFINITY;
+       timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
 
        while (from_iter) {
                add_object_array(&from_iter->item->object, NULL, &from_objs);
 
                if (!parse_commit(from_iter->item)) {
-                       uint32_t generation;
+                       timestamp_t generation;
                        if (from_iter->item->date < min_commit_date)
                                min_commit_date = from_iter->item->date;
 
@@ -701,7 +701,7 @@ int can_all_from_reach(struct commit_list *from, struct commit_list *to,
 
        while (to_iter) {
                if (!parse_commit(to_iter->item)) {
-                       uint32_t generation;
+                       timestamp_t generation;
                        if (to_iter->item->date < min_commit_date)
                                min_commit_date = to_iter->item->date;
 
@@ -741,13 +741,13 @@ struct commit_list *get_reachable_subset(struct commit **from, int nr_from,
        struct commit_list *found_commits = NULL;
        struct commit **to_last = to + nr_to;
        struct commit **from_last = from + nr_from;
-       uint32_t min_generation = GENERATION_NUMBER_INFINITY;
+       timestamp_t min_generation = GENERATION_NUMBER_INFINITY;
        int num_to_find = 0;
 
        struct prio_queue queue = { compare_commits_by_gen_then_commit_date };
 
        for (item = to; item < to_last; item++) {
-               uint32_t generation;
+               timestamp_t generation;
                struct commit *c = *item;
 
                parse_commit(c);
index b49ad71a3177f7dfb670fedd272e68cc89a03e77..148b56fea50629607d2f0dd196cbd4dec08fdcfd 100644 (file)
@@ -87,7 +87,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
                                 unsigned int with_flag,
                                 unsigned int assign_flag,
                                 time_t min_commit_date,
-                                uint32_t min_generation);
+                                timestamp_t min_generation);
 int can_all_from_reach(struct commit_list *from, struct commit_list *to,
                       int commit_date_cutoff);
 
index fd2831dad3243f32f5bae472f2af6ad15019c67d..6ccd774841c6939eb977696ac6f30c04dd514ce3 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -753,8 +753,8 @@ int compare_commits_by_author_date(const void *a_, const void *b_,
 int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void *unused)
 {
        const struct commit *a = a_, *b = b_;
-       const uint32_t generation_a = commit_graph_generation(a),
-                      generation_b = commit_graph_generation(b);
+       const timestamp_t generation_a = commit_graph_generation(a),
+                         generation_b = commit_graph_generation(b);
 
        /* newer commits first */
        if (generation_a < generation_b)
@@ -995,7 +995,7 @@ static const char *gpg_sig_headers[] = {
        "gpgsig-sha256",
 };
 
-static int do_sign_commit(struct strbuf *buf, const char *keyid)
+int sign_with_header(struct strbuf *buf, const char *keyid)
 {
        struct strbuf sig = STRBUF_INIT;
        int inspos, copypos;
@@ -1035,21 +1035,32 @@ static int do_sign_commit(struct strbuf *buf, const char *keyid)
        return 0;
 }
 
+
+
 int parse_signed_commit(const struct commit *commit,
-                       struct strbuf *payload, struct strbuf *signature)
+                       struct strbuf *payload, struct strbuf *signature,
+                       const struct git_hash_algo *algop)
 {
-
        unsigned long size;
        const char *buffer = get_commit_buffer(commit, &size);
-       int in_signature, saw_signature = -1;
-       const char *line, *tail;
-       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(the_hash_algo)];
-       int gpg_sig_header_len = strlen(gpg_sig_header);
+       int ret = parse_buffer_signed_by_header(buffer, size, payload, signature, algop);
+
+       unuse_commit_buffer(commit, buffer);
+       return ret;
+}
+
+int parse_buffer_signed_by_header(const char *buffer,
+                                 unsigned long size,
+                                 struct strbuf *payload,
+                                 struct strbuf *signature,
+                                 const struct git_hash_algo *algop)
+{
+       int in_signature = 0, saw_signature = 0, other_signature = 0;
+       const char *line, *tail, *p;
+       const char *gpg_sig_header = gpg_sig_headers[hash_algo_by_ptr(algop)];
 
        line = buffer;
        tail = buffer + size;
-       in_signature = 0;
-       saw_signature = 0;
        while (line < tail) {
                const char *sig = NULL;
                const char *next = memchr(line, '\n', tail - line);
@@ -1057,9 +1068,15 @@ int parse_signed_commit(const struct commit *commit,
                next = next ? next + 1 : tail;
                if (in_signature && line[0] == ' ')
                        sig = line + 1;
-               else if (starts_with(line, gpg_sig_header) &&
-                        line[gpg_sig_header_len] == ' ')
-                       sig = line + gpg_sig_header_len + 1;
+               else if (skip_prefix(line, gpg_sig_header, &p) &&
+                        *p == ' ') {
+                       sig = line + strlen(gpg_sig_header) + 1;
+                       other_signature = 0;
+               }
+               else if (starts_with(line, "gpgsig"))
+                       other_signature = 1;
+               else if (other_signature && line[0] != ' ')
+                       other_signature = 0;
                if (sig) {
                        strbuf_add(signature, sig, next - sig);
                        saw_signature = 1;
@@ -1068,12 +1085,12 @@ int parse_signed_commit(const struct commit *commit,
                        if (*line == '\n')
                                /* dump the whole remainder of the buffer */
                                next = tail;
-                       strbuf_add(payload, line, next - line);
+                       if (!other_signature)
+                               strbuf_add(payload, line, next - line);
                        in_signature = 0;
                }
                line = next;
        }
-       unuse_commit_buffer(commit, buffer);
        return saw_signature;
 }
 
@@ -1082,23 +1099,29 @@ int remove_signature(struct strbuf *buf)
        const char *line = buf->buf;
        const char *tail = buf->buf + buf->len;
        int in_signature = 0;
-       const char *sig_start = NULL;
-       const char *sig_end = NULL;
+       struct sigbuf {
+               const char *start;
+               const char *end;
+       } sigs[2], *sigp = &sigs[0];
+       int i;
+       const char *orig_buf = buf->buf;
+
+       memset(sigs, 0, sizeof(sigs));
 
        while (line < tail) {
                const char *next = memchr(line, '\n', tail - line);
                next = next ? next + 1 : tail;
 
                if (in_signature && line[0] == ' ')
-                       sig_end = next;
+                       sigp->end = next;
                else if (starts_with(line, "gpgsig")) {
                        int i;
                        for (i = 1; i < GIT_HASH_NALGOS; i++) {
                                const char *p;
                                if (skip_prefix(line, gpg_sig_headers[i], &p) &&
                                    *p == ' ') {
-                                       sig_start = line;
-                                       sig_end = next;
+                                       sigp->start = line;
+                                       sigp->end = next;
                                        in_signature = 1;
                                }
                        }
@@ -1106,15 +1129,18 @@ int remove_signature(struct strbuf *buf)
                        if (*line == '\n')
                                /* dump the whole remainder of the buffer */
                                next = tail;
+                       if (in_signature && sigp - sigs != ARRAY_SIZE(sigs))
+                               sigp++;
                        in_signature = 0;
                }
                line = next;
        }
 
-       if (sig_start)
-               strbuf_remove(buf, sig_start - buf->buf, sig_end - sig_start);
+       for (i = ARRAY_SIZE(sigs) - 1; i >= 0; i--)
+               if (sigs[i].start)
+                       strbuf_remove(buf, sigs[i].start - orig_buf, sigs[i].end - sigs[i].start);
 
-       return sig_start != NULL;
+       return sigs[0].start != NULL;
 }
 
 static void handle_signed_tag(struct commit *parent, struct commit_extra_header ***tail)
@@ -1122,8 +1148,10 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
        struct merge_remote_desc *desc;
        struct commit_extra_header *mergetag;
        char *buf;
-       unsigned long size, len;
+       unsigned long size;
        enum object_type type;
+       struct strbuf payload = STRBUF_INIT;
+       struct strbuf signature = STRBUF_INIT;
 
        desc = merge_remote_util(parent);
        if (!desc || !desc->obj)
@@ -1131,8 +1159,7 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
        buf = read_object_file(&desc->obj->oid, &type, &size);
        if (!buf || type != OBJ_TAG)
                goto free_return;
-       len = parse_signature(buf, size);
-       if (size == len)
+       if (!parse_signature(buf, size, &payload, &signature))
                goto free_return;
        /*
         * We could verify this signature and either omit the tag when
@@ -1151,6 +1178,8 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
 
        **tail = mergetag;
        *tail = &mergetag->next;
+       strbuf_release(&payload);
+       strbuf_release(&signature);
        return;
 
 free_return:
@@ -1165,7 +1194,7 @@ int check_commit_signature(const struct commit *commit, struct signature_check *
 
        sigc->result = 'N';
 
-       if (parse_signed_commit(commit, &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
                goto out;
        ret = check_signature(payload.buf, payload.len, signature.buf,
                signature.len, sigc);
@@ -1515,7 +1544,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
        if (encoding_is_utf8 && !verify_utf8(&buffer))
                fprintf(stderr, _(commit_utf8_warn));
 
-       if (sign_commit && do_sign_commit(&buffer, sign_commit)) {
+       if (sign_commit && sign_with_header(&buffer, sign_commit)) {
                result = -1;
                goto out;
        }
index ecacf9ade36497d6d30dd14bc926fb7759db020d..49c0f503964ec3bb0e682390b0b409b6a76b0924 100644 (file)
--- a/commit.h
+++ b/commit.h
 #include "commit-slab.h"
 
 #define COMMIT_NOT_FROM_GRAPH 0xFFFFFFFF
-#define GENERATION_NUMBER_INFINITY 0xFFFFFFFF
-#define GENERATION_NUMBER_MAX 0x3FFFFFFF
+#define GENERATION_NUMBER_INFINITY ((1ULL << 63) - 1)
+#define GENERATION_NUMBER_V1_MAX 0x3FFFFFFF
 #define GENERATION_NUMBER_ZERO 0
+#define GENERATION_NUMBER_V2_OFFSET_MAX ((1ULL << 31) - 1)
 
 struct commit_list {
        struct commit *item;
@@ -88,9 +89,10 @@ static inline int repo_parse_commit(struct repository *r, struct commit *item)
        return repo_parse_commit_gently(r, item, 0);
 }
 
-static inline int parse_commit_no_graph(struct commit *commit)
+static inline int repo_parse_commit_no_graph(struct repository *r,
+                                            struct commit *commit)
 {
-       return repo_parse_commit_internal(the_repository, commit, 0, 0);
+       return repo_parse_commit_internal(r, commit, 0, 0);
 }
 
 #ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
@@ -317,7 +319,8 @@ void set_merge_remote_desc(struct commit *commit,
 struct commit *get_merge_parent(const char *name);
 
 int parse_signed_commit(const struct commit *commit,
-                       struct strbuf *message, struct strbuf *signature);
+                       struct strbuf *message, struct strbuf *signature,
+                       const struct git_hash_algo *algop);
 int remove_signature(struct strbuf *buf);
 
 /*
@@ -359,4 +362,13 @@ int compare_commits_by_gen_then_commit_date(const void *a_, const void *b_, void
 LAST_ARG_MUST_BE_NULL
 int run_commit_hook(int editor_is_used, const char *index_file, const char *name, ...);
 
+/* Sign a commit or tag buffer, storing the result in a header. */
+int sign_with_header(struct strbuf *buf, const char *keyid);
+/* Parse the signature out of a header. */
+int parse_buffer_signed_by_header(const char *buffer,
+                                 unsigned long size,
+                                 struct strbuf *payload,
+                                 struct strbuf *signature,
+                                 const struct git_hash_algo *algop);
+
 #endif /* COMMIT_H */
index 136250fbf6c4f7e707c25dba936e2effebd847f2..ec560565a862ccf091d68cf34bf2bb985e4faf9c 100644 (file)
@@ -60,32 +60,46 @@ void probe_utf8_pathname_composition(void)
        strbuf_release(&path);
 }
 
-
-void precompose_argv(int argc, const char **argv)
+static inline const char *precompose_string_if_needed(const char *in)
 {
-       int i = 0;
-       const char *oldarg;
-       char *newarg;
-       iconv_t ic_precompose;
+       size_t inlen;
+       size_t outlen;
+       if (has_non_ascii(in, (size_t)-1, &inlen)) {
+               iconv_t ic_prec;
+               char *out;
+               if (precomposed_unicode < 0)
+                       git_config_get_bool("core.precomposeunicode", &precomposed_unicode);
+               if (precomposed_unicode != 1)
+                       return in;
+               ic_prec = iconv_open(repo_encoding, path_encoding);
+               if (ic_prec == (iconv_t) -1)
+                       return in;
+
+               out = reencode_string_iconv(in, inlen, ic_prec, 0, &outlen);
+               if (out) {
+                       if (outlen == inlen && !memcmp(in, out, outlen))
+                               free(out); /* no need to return indentical */
+                       else
+                               in = out;
+               }
+               iconv_close(ic_prec);
 
-       if (precomposed_unicode != 1)
-               return;
+       }
+       return in;
+}
 
-       ic_precompose = iconv_open(repo_encoding, path_encoding);
-       if (ic_precompose == (iconv_t) -1)
-               return;
+const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix)
+{
+       int i = 0;
 
        while (i < argc) {
-               size_t namelen;
-               oldarg = argv[i];
-               if (has_non_ascii(oldarg, (size_t)-1, &namelen)) {
-                       newarg = reencode_string_iconv(oldarg, namelen, ic_precompose, 0, NULL);
-                       if (newarg)
-                               argv[i] = newarg;
-               }
+               argv[i] = precompose_string_if_needed(argv[i]);
                i++;
        }
-       iconv_close(ic_precompose);
+       if (prefix) {
+               prefix = precompose_string_if_needed(prefix);
+       }
+       return prefix;
 }
 
 
index 6f843d3e1a121cf8f73409bc66898f74a9f14f2c..d70b84665c61faee9e1fe1197c1fe61788d2afb2 100644 (file)
@@ -28,7 +28,7 @@ typedef struct {
        struct dirent_prec_psx *dirent_nfc;
 } PREC_DIR;
 
-void precompose_argv(int argc, const char **argv);
+const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix);
 void probe_utf8_pathname_composition(void);
 
 PREC_DIR *precompose_utf8_opendir(const char *dirname);
index b922b4f28572890f402226faccd1dd48a8fc9476..f90b633dba21f3cf63698e62170dab15e316e3e5 100644 (file)
--- a/config.c
+++ b/config.c
@@ -1180,6 +1180,20 @@ static void die_bad_number(const char *name, const char *value)
        }
 }
 
+NORETURN
+static void die_bad_bool(const char *name, const char *value)
+{
+       if (!strcmp(name, "GIT_TEST_GETTEXT_POISON"))
+               /*
+                * We explicitly *don't* use _() here since it would
+                * cause an infinite loop with _() needing to call
+                * use_gettext_poison().
+                */
+               die("bad boolean config value '%s' for '%s'", value, name);
+       else
+               die(_("bad boolean config value '%s' for '%s'"), value, name);
+}
+
 int git_config_int(const char *name, const char *value)
 {
        int ret;
@@ -1252,8 +1266,10 @@ int git_config_bool_or_int(const char *name, const char *value, int *is_bool)
 
 int git_config_bool(const char *name, const char *value)
 {
-       int discard;
-       return !!git_config_bool_or_int(name, value, &discard);
+       int v = git_parse_maybe_bool(value);
+       if (v < 0)
+               die_bad_bool(name, value);
+       return v;
 }
 
 int git_config_string(const char **dest, const char *var, const char *value)
index 9c97fee43031056dff2beb2a8fe9ed882e476c2d..40b5c15f811305648e22e0c2db55fbd1998cf211 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -376,7 +376,8 @@ struct ref **get_remote_heads(struct packet_reader *reader,
 }
 
 /* Returns 1 when a valid ref has been added to `list`, 0 otherwise */
-static int process_ref_v2(struct packet_reader *reader, struct ref ***list)
+static int process_ref_v2(struct packet_reader *reader, struct ref ***list,
+                         char **unborn_head_target)
 {
        int ret = 1;
        int i = 0;
@@ -397,6 +398,25 @@ static int process_ref_v2(struct packet_reader *reader, struct ref ***list)
                goto out;
        }
 
+       if (!strcmp("unborn", line_sections.items[i].string)) {
+               i++;
+               if (unborn_head_target &&
+                   !strcmp("HEAD", line_sections.items[i++].string)) {
+                       /*
+                        * Look for the symref target (if any). If found,
+                        * return it to the caller.
+                        */
+                       for (; i < line_sections.nr; i++) {
+                               const char *arg = line_sections.items[i].string;
+
+                               if (skip_prefix(arg, "symref-target:", &arg)) {
+                                       *unborn_head_target = xstrdup(arg);
+                                       break;
+                               }
+                       }
+               }
+               goto out;
+       }
        if (parse_oid_hex_algop(line_sections.items[i++].string, &old_oid, &end, reader->hash_algo) ||
            *end) {
                ret = 0;
@@ -453,12 +473,16 @@ void check_stateless_delimiter(int stateless_rpc,
 
 struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                             struct ref **list, int for_push,
-                            const struct strvec *ref_prefixes,
+                            struct transport_ls_refs_options *transport_options,
                             const struct string_list *server_options,
                             int stateless_rpc)
 {
        int i;
        const char *hash_name;
+       struct strvec *ref_prefixes = transport_options ?
+               &transport_options->ref_prefixes : NULL;
+       char **unborn_head_target = transport_options ?
+               &transport_options->unborn_head_target : NULL;
        *list = NULL;
 
        if (server_supports_v2("ls-refs", 1))
@@ -488,6 +512,8 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
        if (!for_push)
                packet_write_fmt(fd_out, "peel\n");
        packet_write_fmt(fd_out, "symrefs\n");
+       if (server_supports_feature("ls-refs", "unborn", 0))
+               packet_write_fmt(fd_out, "unborn\n");
        for (i = 0; ref_prefixes && i < ref_prefixes->nr; i++) {
                packet_write_fmt(fd_out, "ref-prefix %s\n",
                                 ref_prefixes->v[i]);
@@ -496,7 +522,7 @@ struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
 
        /* Process response from server */
        while (packet_reader_read(reader) == PACKET_READ_NORMAL) {
-               if (!process_ref_v2(reader, &list))
+               if (!process_ref_v2(reader, &list, unborn_head_target))
                        die(_("invalid ls-refs response: %s"), reader->line);
        }
 
index 4b1f4264a64e6d8bf4c09fae8573f86726f5cad1..7dc6cd8eb8381d66a75e443970adb768befe866c 100644 (file)
@@ -1447,8 +1447,10 @@ _git_branch ()
        while [ $c -lt $cword ]; do
                i="${words[c]}"
                case "$i" in
-               -d|--delete|-m|--move)  only_local_ref="y" ;;
-               -r|--remotes)           has_r="y" ;;
+               -d|-D|--delete|-m|-M|--move|-c|-C|--copy)
+                       only_local_ref="y" ;;
+               -r|--remotes)
+                       has_r="y" ;;
                esac
                ((c++))
        done
diff --git a/diff.c b/diff.c
index 71e4738548421a6137546f0c693a6a95464b89b8..6956f5e335c235d0a1a2ea6d8b7b8e7b7678deda 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -6355,6 +6355,32 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
        }
 }
 
+static void diff_free_file(struct diff_options *options)
+{
+       if (options->close_file)
+               fclose(options->file);
+}
+
+static void diff_free_ignore_regex(struct diff_options *options)
+{
+       int i;
+
+       for (i = 0; i < options->ignore_regex_nr; i++) {
+               regfree(options->ignore_regex[i]);
+               free(options->ignore_regex[i]);
+       }
+       free(options->ignore_regex);
+}
+
+void diff_free(struct diff_options *options)
+{
+       if (options->no_free)
+               return;
+
+       diff_free_file(options);
+       diff_free_ignore_regex(options);
+}
+
 void diff_flush(struct diff_options *options)
 {
        struct diff_queue_struct *q = &diff_queued_diff;
@@ -6418,8 +6444,7 @@ void diff_flush(struct diff_options *options)
                 * options->file to /dev/null should be safe, because we
                 * aren't supposed to produce any output anyway.
                 */
-               if (options->close_file)
-                       fclose(options->file);
+               diff_free_file(options);
                options->file = xfopen("/dev/null", "w");
                options->close_file = 1;
                options->color_moved = 0;
@@ -6452,8 +6477,7 @@ void diff_flush(struct diff_options *options)
 free_queue:
        free(q->queue);
        DIFF_QUEUE_CLEAR(q);
-       if (options->close_file)
-               fclose(options->file);
+       diff_free(options);
 
        /*
         * Report the content-level differences with HAS_CHANGES;
diff --git a/diff.h b/diff.h
index 45300e3597f275bf4e4a0c84e96ca3e669767683..c8f3faea8aa9ad9d627ec61326d89cd8f2ea27c5 100644 (file)
--- a/diff.h
+++ b/diff.h
  * - Once you finish feeding the pairs of files, call `diffcore_std()`.
  * This will tell the diffcore library to go ahead and do its work.
  *
- * - Calling `diff_flush()` will produce the output.
+ * - Calling `diff_flush()` will produce the output, it will call
+ *   `diff_free()` to free any resources, e.g. those allocated in
+ *   `diff_opt_parse()`.
+ *
+ * - Set `.no_free = 1` before calling `diff_flush()` to defer the
+ *   freeing of allocated memory in diff_options. This is useful when
+ *   `diff_flush()` is being called in a loop, rather than as a
+ *   one-off. When setting `.no_free = 1` you must ensure that
+ *   `diff_free()` is called at the end, either by flipping the flag
+ *   before the last `diff_flush()` call, or by flipping it before
+ *   calling `diff_free()` yourself.
  */
 
 struct combine_diff_path;
@@ -386,6 +396,8 @@ struct diff_options {
 
        struct repository *repo;
        struct option *parseopts;
+
+       int no_free;
 };
 
 unsigned diff_filter_bit(char status);
@@ -580,6 +592,7 @@ void diffcore_fix_diff_index(void);
 
 int diff_queue_is_empty(void);
 void diff_flush(struct diff_options*);
+void diff_free(struct diff_options*);
 void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
 
 /* diff-raw status letters */
index 90db9ebd6d06c852333b0f1f2a0097697dcbbea7..8fe6c9384bcb5b63458e832f15bb7025a43647b7 100644 (file)
@@ -465,6 +465,7 @@ void diffcore_rename(struct diff_options *options)
        int num_destinations, dst_cnt;
        struct progress *progress = NULL;
 
+       trace2_region_enter("diff", "setup", options->repo);
        if (!minimum_score)
                minimum_score = DEFAULT_RENAME_SCORE;
 
@@ -510,14 +511,17 @@ void diffcore_rename(struct diff_options *options)
                        register_rename_src(p);
                }
        }
+       trace2_region_leave("diff", "setup", options->repo);
        if (rename_dst_nr == 0 || rename_src_nr == 0)
                goto cleanup; /* nothing to do */
 
+       trace2_region_enter("diff", "exact renames", options->repo);
        /*
         * We really want to cull the candidates list early
         * with cheap tests in order to avoid doing deltas.
         */
        rename_count = find_exact_renames(options);
+       trace2_region_leave("diff", "exact renames", options->repo);
 
        /* Did we only want exact renames? */
        if (minimum_score == MAX_SCORE)
@@ -545,6 +549,7 @@ void diffcore_rename(struct diff_options *options)
                break;
        }
 
+       trace2_region_enter("diff", "inexact renames", options->repo);
        if (options->show_rename_progress) {
                progress = start_delayed_progress(
                                _("Performing inexact rename detection"),
@@ -600,11 +605,13 @@ void diffcore_rename(struct diff_options *options)
        if (detect_rename == DIFF_DETECT_COPY)
                rename_count += find_renames(mx, dst_cnt, minimum_score, 1);
        free(mx);
+       trace2_region_leave("diff", "inexact renames", options->repo);
 
  cleanup:
        /* At this point, we have found some renames and copies and they
         * are recorded in rename_dst.  The original list is still in *q.
         */
+       trace2_region_enter("diff", "write back to queue", options->repo);
        DIFF_QUEUE_CLEAR(&outq);
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
@@ -680,5 +687,6 @@ void diffcore_rename(struct diff_options *options)
                strintmap_clear(break_idx);
                FREE_AND_NULL(break_idx);
        }
+       trace2_region_leave("diff", "write back to queue", options->repo);
        return;
 }
diff --git a/entry.c b/entry.c
index a0532f1f00007be7c9b675a54a7e8c48c000d638..7b9f43716f76e6e7a701966045429b263cfb691e 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -282,7 +282,7 @@ static int write_entry(struct cache_entry *ce,
                new_blob = read_blob_entry(ce, &size);
                if (!new_blob)
                        return error("unable to read sha1 file of %s (%s)",
-                                    path, oid_to_hex(&ce->oid));
+                                    ce->name, oid_to_hex(&ce->oid));
 
                /*
                 * We can't make a real symlink; write out a regular file entry
@@ -309,7 +309,7 @@ static int write_entry(struct cache_entry *ce,
                        new_blob = read_blob_entry(ce, &size);
                        if (!new_blob)
                                return error("unable to read sha1 file of %s (%s)",
-                                            path, oid_to_hex(&ce->oid));
+                                            ce->name, oid_to_hex(&ce->oid));
                }
 
                /*
@@ -354,7 +354,7 @@ static int write_entry(struct cache_entry *ce,
 
        case S_IFGITLINK:
                if (to_tempfile)
-                       return error("cannot create temporary submodule %s", path);
+                       return error("cannot create temporary submodule %s", ce->name);
                if (mkdir(path, 0777) < 0)
                        return error("cannot create submodule directory %s", path);
                sub = submodule_from_ce(ce);
@@ -365,7 +365,7 @@ static int write_entry(struct cache_entry *ce,
                break;
 
        default:
-               return error("unknown file mode for %s in index", path);
+               return error("unknown file mode for %s in index", ce->name);
        }
 
 finish:
index 46f6015c447dad5d2a6768bf92274cf728725846..1e51492a05085f4e76bed5a32d72a6e2049b1271 100644 (file)
@@ -510,22 +510,28 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
        for (i = 0; i < origins.nr; i++) {
                struct object_id *oid = origins.items[i].util;
                enum object_type type;
-               unsigned long size, len;
+               unsigned long size;
                char *buf = read_object_file(oid, &type, &size);
+               char *origbuf = buf;
+               unsigned long len = size;
                struct signature_check sigc = { NULL };
-               struct strbuf sig = STRBUF_INIT;
+               struct strbuf payload = STRBUF_INIT, sig = STRBUF_INIT;
 
                if (!buf || type != OBJ_TAG)
                        goto next;
-               len = parse_signature(buf, size);
 
-               if (size == len)
-                       ; /* merely annotated */
-               else if (check_signature(buf, len, buf + len, size - len, &sigc) &&
-                       !sigc.gpg_output)
-                       strbuf_addstr(&sig, "gpg verification failed.\n");
-               else
-                       strbuf_addstr(&sig, sigc.gpg_output);
+               if (!parse_signature(buf, size, &payload, &sig))
+                       ;/* merely annotated */
+               else {
+                       buf = payload.buf;
+                       len = payload.len;
+                       if (check_signature(payload.buf, payload.len, sig.buf,
+                                        sig.len, &sigc) &&
+                               !sigc.gpg_output)
+                               strbuf_addstr(&sig, "gpg verification failed.\n");
+                       else
+                               strbuf_addstr(&sig, sigc.gpg_output);
+               }
                signature_check_clear(&sigc);
 
                if (!tag_number++) {
@@ -548,9 +554,10 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
                                        strlen(origins.items[i].string));
                        fmt_tag_signature(&tagbuf, &sig, buf, len);
                }
+               strbuf_release(&payload);
                strbuf_release(&sig);
        next:
-               free(buf);
+               free(origbuf);
        }
        if (tagbuf.len) {
                strbuf_addch(out, '\n');
diff --git a/fsck.c b/fsck.c
index 4b7f0b73d73c3aec8342b9bcb4023ec6f0d300a0..71134fdefaa561ced0cfa3659eaa34155e9be5ce 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -463,6 +463,11 @@ static int fsck_walk_commit(struct commit *commit, void *data, struct fsck_optio
                                generation += power * (name[--len] - '0');
                        if (power > 1 && len && name[len - 1] == '~')
                                name_prefix_len = len - 1;
+                       else {
+                               /* Maybe a non-first parent, e.g. HEAD^2 */
+                               generation = 0;
+                               name_prefix_len = len;
+                       }
                }
        }
 
index 1f3f6e9fc5684d3e9dd2a0ac0260556955c9fa4b..6a7afaea8da09fa3fb3670d1a688421fbf152747 100755 (executable)
@@ -39,21 +39,6 @@ _x40="$_x40$_x40$_x40$_x40$_x40$_x40$_x40$_x40"
 TERM_BAD=bad
 TERM_GOOD=good
 
-bisect_skip() {
-       all=''
-       for arg in "$@"
-       do
-               case "$arg" in
-               *..*)
-                       revs=$(git rev-list "$arg") || die "$(eval_gettext "Bad rev input: \$arg")" ;;
-               *)
-                       revs=$(git rev-parse --sq-quote "$arg") ;;
-               esac
-               all="$all $revs"
-       done
-       eval git bisect--helper --bisect-state 'skip' $all
-}
-
 bisect_visualize() {
        git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
 
@@ -77,38 +62,6 @@ bisect_visualize() {
        eval '"$@"' --bisect -- $(cat "$GIT_DIR/BISECT_NAMES")
 }
 
-bisect_replay () {
-       file="$1"
-       test "$#" -eq 1 || die "$(gettext "No logfile given")"
-       test -r "$file" || die "$(eval_gettext "cannot read \$file for replaying")"
-       git bisect--helper --bisect-reset || exit
-       oIFS="$IFS" IFS="$IFS$(printf '\015')"
-       while read git bisect command rev tail
-       do
-               test "$git $bisect" = "git bisect" || test "$git" = "git-bisect" || continue
-               if test "$git" = "git-bisect"
-               then
-                       rev="$command"
-                       command="$bisect"
-               fi
-               get_terms
-               git bisect--helper --check-and-set-terms "$command" "$TERM_GOOD" "$TERM_BAD" || exit
-               get_terms
-               case "$command" in
-               start)
-                       eval "git bisect--helper --bisect-start $rev $tail" ;;
-               "$TERM_GOOD"|"$TERM_BAD"|skip)
-                       git bisect--helper --bisect-write "$command" "$rev" "$TERM_GOOD" "$TERM_BAD" || exit;;
-               terms)
-                       git bisect--helper --bisect-terms $rev || exit;;
-               *)
-                       die "$(gettext "?? what are you talking about?")" ;;
-               esac
-       done <"$file"
-       IFS="$oIFS"
-       git bisect--helper --bisect-auto-next || exit
-}
-
 bisect_run () {
        git bisect--helper --bisect-next-check $TERM_GOOD $TERM_BAD fail || exit
 
@@ -169,11 +122,6 @@ exit code \$res from '\$command' is < 0 or >= 128" >&2
        done
 }
 
-bisect_log () {
-       test -s "$GIT_DIR/BISECT_LOG" || die "$(gettext "We are not bisecting.")"
-       cat "$GIT_DIR/BISECT_LOG"
-}
-
 get_terms () {
        if test -s "$GIT_DIR/BISECT_TERMS"
        then
@@ -199,7 +147,7 @@ case "$#" in
        bad|good|new|old|"$TERM_BAD"|"$TERM_GOOD")
                git bisect--helper --bisect-state "$cmd" "$@" ;;
        skip)
-               bisect_skip "$@" ;;
+               git bisect--helper --bisect-skip "$@" || exit;;
        next)
                # Not sure we want "next" at the UI level anymore.
                git bisect--helper --bisect-next "$@" || exit ;;
@@ -208,9 +156,9 @@ case "$#" in
        reset)
                git bisect--helper --bisect-reset "$@" ;;
        replay)
-               bisect_replay "$@" ;;
+               git bisect--helper --bisect-replay "$@" || exit;;
        log)
-               bisect_log ;;
+               git bisect--helper --bisect-log || exit ;;
        run)
                bisect_run "$@" ;;
        terms)
index 5d5e47fbe2d66997f569301eadd462809c0cab92..838246289c2902ee12b1d549cb3b3912a6fadae9 100644 (file)
@@ -252,9 +252,9 @@ typedef unsigned long uintptr_t;
 #ifdef PRECOMPOSE_UNICODE
 #include "compat/precompose_utf8.h"
 #else
-static inline void precompose_argv(int argc, const char **argv)
+static inline const char *precompose_argv_prefix(int argc, const char **argv, const char *prefix)
 {
-       ; /* nothing */
+       return prefix;
 }
 #define probe_utf8_pathname_composition()
 #endif
index 46af3e60b7185b2767accbee3420903edaf53d6f..992124cc67ce579e89ae86e4cca42ba2c80ea1ea 100755 (executable)
@@ -61,6 +61,9 @@ launch_merge_tool () {
                export BASE
                eval $GIT_DIFFTOOL_EXTCMD '"$LOCAL"' '"$REMOTE"'
        else
+               initialize_merge_tool "$merge_tool"
+               # ignore the error from the above --- run_merge_tool
+               # will diagnose unusable tool by itself
                run_merge_tool "$merge_tool"
        fi
 }
@@ -79,6 +82,9 @@ if test -n "$GIT_DIFFTOOL_DIRDIFF"
 then
        LOCAL="$1"
        REMOTE="$2"
+       initialize_merge_tool "$merge_tool"
+       # ignore the error from the above --- run_merge_tool
+       # will diagnose unusable tool by itself
        run_merge_tool "$merge_tool" false
 else
        # Launch the merge tool on each path provided by 'git diff'
index 78f3647ed974b025ff87eaada0c2880723f074df..542a6a75eb3c4b93d8b4a394551d5d38d2a88f6e 100644 (file)
@@ -166,6 +166,10 @@ setup_tool () {
                return 1
        }
 
+       hide_resolved_enabled () {
+               return 0
+       }
+
        translate_merge_tool_path () {
                echo "$1"
        }
@@ -250,6 +254,10 @@ trust_exit_code () {
        fi
 }
 
+initialize_merge_tool () {
+       # Bring tool-specific functions into scope
+       setup_tool "$1" || return 1
+}
 
 # Entry point for running tools
 run_merge_tool () {
@@ -261,9 +269,6 @@ run_merge_tool () {
        merge_tool_path=$(get_merge_tool_path "$1") || exit
        base_present="$2"
 
-       # Bring tool-specific functions into scope
-       setup_tool "$1" || return 1
-
        if merge_mode
        then
                run_merge_cmd "$1"
index e3f6d543fb5bb0777483081fadaa7b66035ab5fa..911470a5b2c7aac6224964b7b0f39a3d72a9a985 100755 (executable)
@@ -239,6 +239,13 @@ checkout_staged_file () {
        fi
 }
 
+hide_resolved () {
+       git merge-file --ours -q -p "$LOCAL" "$BASE" "$REMOTE" >"$LCONFL"
+       git merge-file --theirs -q -p "$LOCAL" "$BASE" "$REMOTE" >"$RCONFL"
+       mv -- "$LCONFL" "$LOCAL"
+       mv -- "$RCONFL" "$REMOTE"
+}
+
 merge_file () {
        MERGED="$1"
 
@@ -265,6 +272,8 @@ merge_file () {
                ext=
        esac
 
+       initialize_merge_tool "$merge_tool" || return
+
        mergetool_tmpdir_init
 
        if test "$MERGETOOL_TMPDIR" != "."
@@ -276,7 +285,9 @@ merge_file () {
 
        BACKUP="$MERGETOOL_TMPDIR/${BASE}_BACKUP_$$$ext"
        LOCAL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_$$$ext"
+       LCONFL="$MERGETOOL_TMPDIR/${BASE}_LOCAL_LCONFL_$$$ext"
        REMOTE="$MERGETOOL_TMPDIR/${BASE}_REMOTE_$$$ext"
+       RCONFL="$MERGETOOL_TMPDIR/${BASE}_REMOTE_RCONFL_$$$ext"
        BASE="$MERGETOOL_TMPDIR/${BASE}_BASE_$$$ext"
 
        base_mode= local_mode= remote_mode=
@@ -322,6 +333,45 @@ merge_file () {
        checkout_staged_file 2 "$MERGED" "$LOCAL"
        checkout_staged_file 3 "$MERGED" "$REMOTE"
 
+       # hideResolved preferences hierarchy.
+       global_config="mergetool.hideResolved"
+       tool_config="mergetool.${merge_tool}.hideResolved"
+
+       if enabled=$(git config --type=bool "$tool_config")
+       then
+               # The user has a specific preference for a specific tool and no
+               # other preferences should override that.
+               : ;
+       elif enabled=$(git config --type=bool "$global_config")
+       then
+               # The user has a general preference for all tools.
+               #
+               # 'true' means the user likes the feature so we should use it
+               # where possible but tool authors can still override.
+               #
+               # 'false' means the user doesn't like the feature so we should
+               # not use it anywhere.
+               if test "$enabled" = true && hide_resolved_enabled
+               then
+                   enabled=true
+               else
+                   enabled=false
+               fi
+       else
+               # The user does not have a preference. Ask the tool.
+               if hide_resolved_enabled
+               then
+                   enabled=true
+               else
+                   enabled=false
+               fi
+       fi
+
+       if test "$enabled" = true
+       then
+               hide_resolved
+       fi
+
        if test -z "$local_mode" || test -z "$remote_mode"
        then
                echo "Deleted merge conflict for '$MERGED':"
diff --git a/git.c b/git.c
index b5f63d346b6072e6334cc495022d8a1de2a4e42a..9bc077a025cba4c5b3628b0eabb4d3aac0f35c63 100644 (file)
--- a/git.c
+++ b/git.c
@@ -423,7 +423,7 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
                        int nongit_ok;
                        prefix = setup_git_directory_gently(&nongit_ok);
                }
-
+               prefix = precompose_argv_prefix(argc, argv, prefix);
                if (use_pager == -1 && p->option & (RUN_SETUP | RUN_SETUP_GENTLY) &&
                    !(p->option & DELAY_PAGER_CONFIG))
                        use_pager = check_pager_config(p->cmd);
index b49927083661c8ca695112b8432691f17ed6d835..127aecfc2b071f9a745a871a9ea205931eeb672f 100644 (file)
@@ -1,4 +1,5 @@
 #include "cache.h"
+#include "commit.h"
 #include "config.h"
 #include "run-command.h"
 #include "strbuf.h"
@@ -345,7 +346,7 @@ void print_signature_buffer(const struct signature_check *sigc, unsigned flags)
                fputs(output, stderr);
 }
 
-size_t parse_signature(const char *buf, size_t size)
+size_t parse_signed_buffer(const char *buf, size_t size)
 {
        size_t len = 0;
        size_t match = size;
@@ -361,6 +362,18 @@ size_t parse_signature(const char *buf, size_t size)
        return match;
 }
 
+int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct strbuf *signature)
+{
+       size_t match = parse_signed_buffer(buf, size);
+       if (match != size) {
+               strbuf_add(payload, buf, match);
+               remove_signature(payload);
+               strbuf_add(signature, buf + match, size - match);
+               return 1;
+       }
+       return 0;
+}
+
 void set_signing_key(const char *key)
 {
        free(configured_signing_key);
index f4e9b4f3715a0b0d4be3ef526388bf6b87b56fda..80567e4894868d5d7a192cab0afb9ca9c09cb70d 100644 (file)
@@ -37,13 +37,20 @@ struct signature_check {
 
 void signature_check_clear(struct signature_check *sigc);
 
+/*
+ * Look at a GPG signed tag object.  If such a signature exists, store it in
+ * signature and the signed content in payload.  Return 1 if a signature was
+ * found, and 0 otherwise.
+ */
+int parse_signature(const char *buf, size_t size, struct strbuf *payload, struct strbuf *signature);
+
 /*
  * Look at GPG signed content (e.g. a signed tag object), whose
  * payload is followed by a detached signature on it.  Return the
  * offset where the embedded detached signature begins, or the end of
  * the data when there is no such signature.
  */
-size_t parse_signature(const char *buf, size_t size);
+size_t parse_signed_buffer(const char *buf, size_t size);
 
 /*
  * Create a detached signature for the contents of "buffer" and append
index e048467650770510b41afb92678d0a807c34764d..4531cebfab38c047fcdffb3ac0acaec747f688f1 100644 (file)
@@ -502,7 +502,7 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
        struct signature_check sigc = { 0 };
        int status;
 
-       if (parse_signed_commit(commit, &payload, &signature) <= 0)
+       if (parse_signed_commit(commit, &payload, &signature, the_hash_algo) <= 0)
                goto out;
 
        status = check_signature(payload.buf, payload.len, signature.buf,
@@ -548,7 +548,8 @@ static int show_one_mergetag(struct commit *commit,
        struct strbuf verify_message;
        struct signature_check sigc = { 0 };
        int status, nth;
-       size_t payload_size;
+       struct strbuf payload = STRBUF_INIT;
+       struct strbuf signature = STRBUF_INIT;
 
        hash_object_file(the_hash_algo, extra->value, extra->len,
                         type_name(OBJ_TAG), &oid);
@@ -571,13 +572,11 @@ static int show_one_mergetag(struct commit *commit,
                strbuf_addf(&verify_message,
                            "parent #%d, tagged '%s'\n", nth + 1, tag->tag);
 
-       payload_size = parse_signature(extra->value, extra->len);
        status = -1;
-       if (extra->len > payload_size) {
+       if (parse_signature(extra->value, extra->len, &payload, &signature)) {
                /* could have a good signature */
-               status = check_signature(extra->value, payload_size,
-                                        extra->value + payload_size,
-                                        extra->len - payload_size, &sigc);
+               status = check_signature(payload.buf, payload.len,
+                                        signature.buf, signature.len, &sigc);
                if (sigc.gpg_output)
                        strbuf_addstr(&verify_message, sigc.gpg_output);
                else
@@ -588,6 +587,8 @@ static int show_one_mergetag(struct commit *commit,
 
        show_sig_lines(opt, status, verify_message.buf);
        strbuf_release(&verify_message);
+       strbuf_release(&payload);
+       strbuf_release(&signature);
        return 0;
 }
 
@@ -808,6 +809,11 @@ void show_log(struct rev_info *opt)
        if (cmit_fmt_is_mail(ctx.fmt) && opt->rdiff1) {
                struct diff_queue_struct dq;
                struct diff_options opts;
+               struct range_diff_options range_diff_opts = {
+                       .creation_factor = opt->creation_factor,
+                       .dual_color = 1,
+                       .diffopt = &opts
+               };
 
                memcpy(&dq, &diff_queued_diff, sizeof(diff_queued_diff));
                DIFF_QUEUE_CLEAR(&diff_queued_diff);
@@ -822,8 +828,7 @@ void show_log(struct rev_info *opt)
                opts.file = opt->diffopt.file;
                opts.use_color = opt->diffopt.use_color;
                diff_setup_done(&opts);
-               show_range_diff(opt->rdiff1, opt->rdiff2,
-                               opt->creation_factor, 1, &opts, NULL);
+               show_range_diff(opt->rdiff1, opt->rdiff2, &range_diff_opts);
 
                memcpy(&diff_queued_diff, &dq, sizeof(diff_queued_diff));
        }
@@ -958,12 +963,14 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
 int log_tree_commit(struct rev_info *opt, struct commit *commit)
 {
        struct log_info log;
-       int shown, close_file = opt->diffopt.close_file;
+       int shown;
+       /* maybe called by e.g. cmd_log_walk(), maybe stand-alone */
+       int no_free = opt->diffopt.no_free;
 
        log.commit = commit;
        log.parent = NULL;
        opt->loginfo = &log;
-       opt->diffopt.close_file = 0;
+       opt->diffopt.no_free = 1;
 
        if (opt->line_level_traverse)
                return line_log_print(opt, commit);
@@ -980,7 +987,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
                fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
        opt->loginfo = NULL;
        maybe_flush_or_die(opt->diffopt.file, "stdout");
-       if (close_file)
-               fclose(opt->diffopt.file);
+       opt->diffopt.no_free = no_free;
+       diff_free(&opt->diffopt);
        return shown;
 }
index 5ff5473869fa19aa08d655cb1dcc7485d71dbbbd..88f6c3f60d8ef61373a1bfed33f49c877b6af320 100644 (file)
--- a/ls-refs.c
+++ b/ls-refs.c
@@ -7,6 +7,39 @@
 #include "pkt-line.h"
 #include "config.h"
 
+static int config_read;
+static int advertise_unborn;
+static int allow_unborn;
+
+static void ensure_config_read(void)
+{
+       const char *str = NULL;
+
+       if (config_read)
+               return;
+
+       if (repo_config_get_string_tmp(the_repository, "lsrefs.unborn", &str)) {
+               /*
+                * If there is no such config, advertise and allow it by
+                * default.
+                */
+               advertise_unborn = 1;
+               allow_unborn = 1;
+       } else {
+               if (!strcmp(str, "advertise")) {
+                       advertise_unborn = 1;
+                       allow_unborn = 1;
+               } else if (!strcmp(str, "allow")) {
+                       allow_unborn = 1;
+               } else if (!strcmp(str, "ignore")) {
+                       /* do nothing */
+               } else {
+                       die(_("invalid value '%s' for lsrefs.unborn"), str);
+               }
+       }
+       config_read = 1;
+}
+
 /*
  * Check if one of the prefixes is a prefix of the ref.
  * If no prefixes were provided, all refs match.
@@ -32,6 +65,7 @@ struct ls_refs_data {
        unsigned peel;
        unsigned symrefs;
        struct strvec prefixes;
+       unsigned unborn : 1;
 };
 
 static int send_ref(const char *refname, const struct object_id *oid,
@@ -47,7 +81,10 @@ static int send_ref(const char *refname, const struct object_id *oid,
        if (!ref_match(&data->prefixes, refname_nons))
                return 0;
 
-       strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
+       if (oid)
+               strbuf_addf(&refline, "%s %s", oid_to_hex(oid), refname_nons);
+       else
+               strbuf_addf(&refline, "unborn %s", refname_nons);
        if (data->symrefs && flag & REF_ISSYMREF) {
                struct object_id unused;
                const char *symref_target = resolve_ref_unsafe(refname, 0,
@@ -61,7 +98,7 @@ static int send_ref(const char *refname, const struct object_id *oid,
                            strip_namespace(symref_target));
        }
 
-       if (data->peel) {
+       if (data->peel && oid) {
                struct object_id peeled;
                if (!peel_iterated_oid(oid, &peeled))
                        strbuf_addf(&refline, " peeled:%s", oid_to_hex(&peeled));
@@ -74,6 +111,23 @@ static int send_ref(const char *refname, const struct object_id *oid,
        return 0;
 }
 
+static void send_possibly_unborn_head(struct ls_refs_data *data)
+{
+       struct strbuf namespaced = STRBUF_INIT;
+       struct object_id oid;
+       int flag;
+       int oid_is_null;
+
+       strbuf_addf(&namespaced, "%sHEAD", get_git_namespace());
+       if (!resolve_ref_unsafe(namespaced.buf, 0, &oid, &flag))
+               return; /* bad ref */
+       oid_is_null = is_null_oid(&oid);
+       if (!oid_is_null ||
+           (data->unborn && data->symrefs && (flag & REF_ISSYMREF)))
+               send_ref(namespaced.buf, oid_is_null ? NULL : &oid, flag, data);
+       strbuf_release(&namespaced);
+}
+
 static int ls_refs_config(const char *var, const char *value, void *data)
 {
        /*
@@ -92,6 +146,7 @@ int ls_refs(struct repository *r, struct strvec *keys,
        memset(&data, 0, sizeof(data));
        strvec_init(&data.prefixes);
 
+       ensure_config_read();
        git_config(ls_refs_config, NULL);
 
        while (packet_reader_read(request) == PACKET_READ_NORMAL) {
@@ -104,12 +159,14 @@ int ls_refs(struct repository *r, struct strvec *keys,
                        data.symrefs = 1;
                else if (skip_prefix(arg, "ref-prefix ", &out))
                        strvec_push(&data.prefixes, out);
+               else if (!strcmp("unborn", arg))
+                       data.unborn = allow_unborn;
        }
 
        if (request->status != PACKET_READ_FLUSH)
                die(_("expected flush after ls-refs arguments"));
 
-       head_ref_namespaced(send_ref, &data);
+       send_possibly_unborn_head(&data);
        if (!data.prefixes.nr)
                strvec_push(&data.prefixes, "");
        for_each_fullref_in_prefixes(get_git_namespace(), data.prefixes.v,
@@ -118,3 +175,14 @@ int ls_refs(struct repository *r, struct strvec *keys,
        strvec_clear(&data.prefixes);
        return 0;
 }
+
+int ls_refs_advertise(struct repository *r, struct strbuf *value)
+{
+       if (value) {
+               ensure_config_read();
+               if (advertise_unborn)
+                       strbuf_addstr(value, "unborn");
+       }
+
+       return 1;
+}
index 7b33a7c6b819c0d504ad6b447b61e003e9f590fb..a99e4be0bdde1b287807459dc40934ef4459b227 100644 (file)
--- a/ls-refs.h
+++ b/ls-refs.h
@@ -6,5 +6,6 @@ struct strvec;
 struct packet_reader;
 int ls_refs(struct repository *r, struct strvec *keys,
            struct packet_reader *request);
+int ls_refs_advertise(struct repository *r, struct strbuf *value);
 
 #endif /* LS_REFS_H */
index eb77c6e77cebf517c989cb0a072e9ead77fd6350..9bb9cf8b3038aa8234accc4832178ee498062c59 100644 (file)
--- a/mailmap.c
+++ b/mailmap.c
@@ -225,7 +225,8 @@ int read_mailmap(struct string_list *map)
        if (!git_mailmap_blob && is_bare_repository())
                git_mailmap_blob = "HEAD:.mailmap";
 
-       err |= read_mailmap_file(map, ".mailmap");
+       if (!startup_info->have_repository || !is_bare_repository())
+               err |= read_mailmap_file(map, ".mailmap");
        if (startup_info->have_repository)
                err |= read_mailmap_blob(map, git_mailmap_blob);
        err |= read_mailmap_file(map, git_mailmap_file);
index 6900ab9e7fcc6c7d595885dd90039828e04083d5..931b91438cf1bb5abaeb98fdd0b3292af66e5bd7 100644 (file)
@@ -52,14 +52,42 @@ enum merge_side {
 };
 
 struct rename_info {
+       /*
+        * All variables that are arrays of size 3 correspond to data tracked
+        * for the sides in enum merge_side.  Index 0 is almost always unused
+        * because we often only need to track information for MERGE_SIDE1 and
+        * MERGE_SIDE2 (MERGE_BASE can't have rename information since renames
+        * are determined relative to what changed since the MERGE_BASE).
+        */
+
        /*
         * pairs: pairing of filenames from diffcore_rename()
-        *
-        * Index 1 and 2 correspond to sides 1 & 2 as used in
-        * conflict_info.stages.  Index 0 unused.
         */
        struct diff_queue_struct pairs[3];
 
+       /*
+        * dirs_removed: directories removed on a given side of history.
+        */
+       struct strset dirs_removed[3];
+
+       /*
+        * dir_rename_count: tracking where parts of a directory were renamed to
+        *
+        * When files in a directory are renamed, they may not all go to the
+        * same location.  Each strmap here tracks:
+        *      old_dir => {new_dir => int}
+        * That is, dir_rename_count[side] is a strmap to a strintmap.
+        */
+       struct strmap dir_rename_count[3];
+
+       /*
+        * dir_renames: computed directory renames
+        *
+        * This is a map of old_dir => new_dir and is derived in part from
+        * dir_rename_count.
+        */
+       struct strmap dir_renames[3];
+
        /*
         * needed_limit: value needed for inexact rename detection to run
         *
@@ -143,12 +171,15 @@ struct merge_options_internal {
        struct rename_info renames;
 
        /*
-        * current_dir_name: temporary var used in collect_merge_info_callback()
+        * current_dir_name, toplevel_dir: temporary vars
         *
-        * Used to set merged_info.directory_name; see documentation for that
-        * variable and the requirements placed on that field.
+        * These are used in collect_merge_info_callback(), and will set the
+        * various merged_info.directory_name for the various paths we get;
+        * see documentation for that variable and the requirements placed on
+        * that field.
         */
        const char *current_dir_name;
+       const char *toplevel_dir;
 
        /* call_depth: recursion level counter for merging merge bases */
        int call_depth;
@@ -283,8 +314,12 @@ static void free_strmap_strings(struct strmap *map)
 static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
                                          int reinitialize)
 {
+       struct rename_info *renames = &opti->renames;
+       int i;
        void (*strmap_func)(struct strmap *, int) =
                reinitialize ? strmap_partial_clear : strmap_clear;
+       void (*strset_func)(struct strset *) =
+               reinitialize ? strset_partial_clear : strset_clear;
 
        /*
         * We marked opti->paths with strdup_strings = 0, so that we
@@ -314,6 +349,23 @@ static void clear_or_reinit_internal_opts(struct merge_options_internal *opti,
        string_list_clear(&opti->paths_to_free, 0);
        opti->paths_to_free.strdup_strings = 0;
 
+       /* Free memory used by various renames maps */
+       for (i = MERGE_SIDE1; i <= MERGE_SIDE2; ++i) {
+               struct hashmap_iter iter;
+               struct strmap_entry *entry;
+
+               strset_func(&renames->dirs_removed[i]);
+
+               strmap_for_each_entry(&renames->dir_rename_count[i],
+                                     &iter, entry) {
+                       struct strintmap *counts = entry->value;
+                       strintmap_clear(counts);
+               }
+               strmap_func(&renames->dir_rename_count[i], 1);
+
+               strmap_func(&renames->dir_renames[i], 0);
+       }
+
        if (!reinitialize) {
                struct hashmap_iter iter;
                struct strmap_entry *e;
@@ -483,6 +535,27 @@ static void setup_path_info(struct merge_options *opt,
        result->util = mi;
 }
 
+static void collect_rename_info(struct merge_options *opt,
+                               struct name_entry *names,
+                               const char *dirname,
+                               const char *fullname,
+                               unsigned filemask,
+                               unsigned dirmask,
+                               unsigned match_mask)
+{
+       struct rename_info *renames = &opt->priv->renames;
+
+       /* Update dirs_removed, as needed */
+       if (dirmask == 1 || dirmask == 3 || dirmask == 5) {
+               /* absent_mask = 0x07 - dirmask; sides = absent_mask/2 */
+               unsigned sides = (0x07 - dirmask)/2;
+               if (sides & 1)
+                       strset_add(&renames->dirs_removed[1], fullname);
+               if (sides & 2)
+                       strset_add(&renames->dirs_removed[2], fullname);
+       }
+}
+
 static int collect_merge_info_callback(int n,
                                       unsigned long mask,
                                       unsigned long dirmask,
@@ -583,6 +656,12 @@ static int collect_merge_info_callback(int n,
                return mask;
        }
 
+       /*
+        * Gather additional information used in rename detection.
+        */
+       collect_rename_info(opt, names, dirname, fullpath,
+                           filemask, dirmask, match_mask);
+
        /*
         * Record information about the path so we can resolve later in
         * process_entries.
@@ -658,10 +737,10 @@ static int collect_merge_info(struct merge_options *opt,
        int ret;
        struct tree_desc t[3];
        struct traverse_info info;
-       const char *toplevel_dir_placeholder = "";
 
-       opt->priv->current_dir_name = toplevel_dir_placeholder;
-       setup_traverse_info(&info, toplevel_dir_placeholder);
+       opt->priv->toplevel_dir = "";
+       opt->priv->current_dir_name = opt->priv->toplevel_dir;
+       setup_traverse_info(&info, opt->priv->toplevel_dir);
        info.fn = collect_merge_info_callback;
        info.data = opt;
        info.show_all_errors = 1;
@@ -673,7 +752,9 @@ static int collect_merge_info(struct merge_options *opt,
        init_tree_desc(t + 1, side1->buffer, side1->size);
        init_tree_desc(t + 2, side2->buffer, side2->size);
 
+       trace2_region_enter("merge", "traverse_trees", opt->repo);
        ret = traverse_trees(NULL, 3, t, &info);
+       trace2_region_leave("merge", "traverse_trees", opt->repo);
 
        return ret;
 }
@@ -1063,6 +1144,649 @@ static int handle_content_merge(struct merge_options *opt,
 
 /*** Function Grouping: functions related to directory rename detection ***/
 
+struct collision_info {
+       struct string_list source_files;
+       unsigned reported_already:1;
+};
+
+/*
+ * Return a new string that replaces the beginning portion (which matches
+ * rename_info->key), with rename_info->util.new_dir.  In perl-speak:
+ *   new_path_name = (old_path =~ s/rename_info->key/rename_info->value/);
+ * NOTE:
+ *   Caller must ensure that old_path starts with rename_info->key + '/'.
+ */
+static char *apply_dir_rename(struct strmap_entry *rename_info,
+                             const char *old_path)
+{
+       struct strbuf new_path = STRBUF_INIT;
+       const char *old_dir = rename_info->key;
+       const char *new_dir = rename_info->value;
+       int oldlen, newlen, new_dir_len;
+
+       oldlen = strlen(old_dir);
+       if (*new_dir == '\0')
+               /*
+                * If someone renamed/merged a subdirectory into the root
+                * directory (e.g. 'some/subdir' -> ''), then we want to
+                * avoid returning
+                *     '' + '/filename'
+                * as the rename; we need to make old_path + oldlen advance
+                * past the '/' character.
+                */
+               oldlen++;
+       new_dir_len = strlen(new_dir);
+       newlen = new_dir_len + (strlen(old_path) - oldlen) + 1;
+       strbuf_grow(&new_path, newlen);
+       strbuf_add(&new_path, new_dir, new_dir_len);
+       strbuf_addstr(&new_path, &old_path[oldlen]);
+
+       return strbuf_detach(&new_path, NULL);
+}
+
+static int path_in_way(struct strmap *paths, const char *path, unsigned side_mask)
+{
+       struct merged_info *mi = strmap_get(paths, path);
+       struct conflict_info *ci;
+       if (!mi)
+               return 0;
+       INITIALIZE_CI(ci, mi);
+       return mi->clean || (side_mask & (ci->filemask | ci->dirmask));
+}
+
+/*
+ * See if there is a directory rename for path, and if there are any file
+ * level conflicts on the given side for the renamed location.  If there is
+ * a rename and there are no conflicts, return the new name.  Otherwise,
+ * return NULL.
+ */
+static char *handle_path_level_conflicts(struct merge_options *opt,
+                                        const char *path,
+                                        unsigned side_index,
+                                        struct strmap_entry *rename_info,
+                                        struct strmap *collisions)
+{
+       char *new_path = NULL;
+       struct collision_info *c_info;
+       int clean = 1;
+       struct strbuf collision_paths = STRBUF_INIT;
+
+       /*
+        * entry has the mapping of old directory name to new directory name
+        * that we want to apply to path.
+        */
+       new_path = apply_dir_rename(rename_info, path);
+       if (!new_path)
+               BUG("Failed to apply directory rename!");
+
+       /*
+        * The caller needs to have ensured that it has pre-populated
+        * collisions with all paths that map to new_path.  Do a quick check
+        * to ensure that's the case.
+        */
+       c_info = strmap_get(collisions, new_path);
+       if (c_info == NULL)
+               BUG("c_info is NULL");
+
+       /*
+        * Check for one-sided add/add/.../add conflicts, i.e.
+        * where implicit renames from the other side doing
+        * directory rename(s) can affect this side of history
+        * to put multiple paths into the same location.  Warn
+        * and bail on directory renames for such paths.
+        */
+       if (c_info->reported_already) {
+               clean = 0;
+       } else if (path_in_way(&opt->priv->paths, new_path, 1 << side_index)) {
+               c_info->reported_already = 1;
+               strbuf_add_separated_string_list(&collision_paths, ", ",
+                                                &c_info->source_files);
+               path_msg(opt, new_path, 0,
+                        _("CONFLICT (implicit dir rename): Existing file/dir "
+                          "at %s in the way of implicit directory rename(s) "
+                          "putting the following path(s) there: %s."),
+                      new_path, collision_paths.buf);
+               clean = 0;
+       } else if (c_info->source_files.nr > 1) {
+               c_info->reported_already = 1;
+               strbuf_add_separated_string_list(&collision_paths, ", ",
+                                                &c_info->source_files);
+               path_msg(opt, new_path, 0,
+                        _("CONFLICT (implicit dir rename): Cannot map more "
+                          "than one path to %s; implicit directory renames "
+                          "tried to put these paths there: %s"),
+                      new_path, collision_paths.buf);
+               clean = 0;
+       }
+
+       /* Free memory we no longer need */
+       strbuf_release(&collision_paths);
+       if (!clean && new_path) {
+               free(new_path);
+               return NULL;
+       }
+
+       return new_path;
+}
+
+static void dirname_munge(char *filename)
+{
+       char *slash = strrchr(filename, '/');
+       if (!slash)
+               slash = filename;
+       *slash = '\0';
+}
+
+static void increment_count(struct strmap *dir_rename_count,
+                           char *old_dir,
+                           char *new_dir)
+{
+       struct strintmap *counts;
+       struct strmap_entry *e;
+
+       /* Get the {new_dirs -> counts} mapping using old_dir */
+       e = strmap_get_entry(dir_rename_count, old_dir);
+       if (e) {
+               counts = e->value;
+       } else {
+               counts = xmalloc(sizeof(*counts));
+               strintmap_init_with_options(counts, 0, NULL, 1);
+               strmap_put(dir_rename_count, old_dir, counts);
+       }
+
+       /* Increment the count for new_dir */
+       strintmap_incr(counts, new_dir, 1);
+}
+
+static void update_dir_rename_counts(struct strmap *dir_rename_count,
+                                    struct strset *dirs_removed,
+                                    const char *oldname,
+                                    const char *newname)
+{
+       char *old_dir = xstrdup(oldname);
+       char *new_dir = xstrdup(newname);
+       char new_dir_first_char = new_dir[0];
+       int first_time_in_loop = 1;
+
+       while (1) {
+               dirname_munge(old_dir);
+               dirname_munge(new_dir);
+
+               /*
+                * When renaming
+                *   "a/b/c/d/e/foo.c" -> "a/b/some/thing/else/e/foo.c"
+                * then this suggests that both
+                *   a/b/c/d/e/ => a/b/some/thing/else/e/
+                *   a/b/c/d/   => a/b/some/thing/else/
+                * so we want to increment counters for both.  We do NOT,
+                * however, also want to suggest that there was the following
+                * rename:
+                *   a/b/c/ => a/b/some/thing/
+                * so we need to quit at that point.
+                *
+                * Note the when first_time_in_loop, we only strip off the
+                * basename, and we don't care if that's different.
+                */
+               if (!first_time_in_loop) {
+                       char *old_sub_dir = strchr(old_dir, '\0')+1;
+                       char *new_sub_dir = strchr(new_dir, '\0')+1;
+                       if (!*new_dir) {
+                               /*
+                                * Special case when renaming to root directory,
+                                * i.e. when new_dir == "".  In this case, we had
+                                * something like
+                                *    a/b/subdir => subdir
+                                * and so dirname_munge() sets things up so that
+                                *    old_dir = "a/b\0subdir\0"
+                                *    new_dir = "\0ubdir\0"
+                                * We didn't have a '/' to overwrite a '\0' onto
+                                * in new_dir, so we have to compare differently.
+                                */
+                               if (new_dir_first_char != old_sub_dir[0] ||
+                                   strcmp(old_sub_dir+1, new_sub_dir))
+                                       break;
+                       } else {
+                               if (strcmp(old_sub_dir, new_sub_dir))
+                                       break;
+                       }
+               }
+
+               if (strset_contains(dirs_removed, old_dir))
+                       increment_count(dir_rename_count, old_dir, new_dir);
+               else
+                       break;
+
+               /* If we hit toplevel directory ("") for old or new dir, quit */
+               if (!*old_dir || !*new_dir)
+                       break;
+
+               first_time_in_loop = 0;
+       }
+
+       /* Free resources we don't need anymore */
+       free(old_dir);
+       free(new_dir);
+}
+
+static void compute_rename_counts(struct diff_queue_struct *pairs,
+                                 struct strmap *dir_rename_count,
+                                 struct strset *dirs_removed)
+{
+       int i;
+
+       for (i = 0; i < pairs->nr; ++i) {
+               struct diff_filepair *pair = pairs->queue[i];
+
+               /* File not part of directory rename if it wasn't renamed */
+               if (pair->status != 'R')
+                       continue;
+
+               /*
+                * Make dir_rename_count contain a map of a map:
+                *   old_directory -> {new_directory -> count}
+                * In other words, for every pair look at the directories for
+                * the old filename and the new filename and count how many
+                * times that pairing occurs.
+                */
+               update_dir_rename_counts(dir_rename_count, dirs_removed,
+                                        pair->one->path,
+                                        pair->two->path);
+       }
+}
+
+static void get_provisional_directory_renames(struct merge_options *opt,
+                                             unsigned side,
+                                             int *clean)
+{
+       struct hashmap_iter iter;
+       struct strmap_entry *entry;
+       struct rename_info *renames = &opt->priv->renames;
+
+       compute_rename_counts(&renames->pairs[side],
+                             &renames->dir_rename_count[side],
+                             &renames->dirs_removed[side]);
+       /*
+        * Collapse
+        *    dir_rename_count: old_directory -> {new_directory -> count}
+        * down to
+        *    dir_renames: old_directory -> best_new_directory
+        * where best_new_directory is the one with the unique highest count.
+        */
+       strmap_for_each_entry(&renames->dir_rename_count[side], &iter, entry) {
+               const char *source_dir = entry->key;
+               struct strintmap *counts = entry->value;
+               struct hashmap_iter count_iter;
+               struct strmap_entry *count_entry;
+               int max = 0;
+               int bad_max = 0;
+               const char *best = NULL;
+
+               strintmap_for_each_entry(counts, &count_iter, count_entry) {
+                       const char *target_dir = count_entry->key;
+                       intptr_t count = (intptr_t)count_entry->value;
+
+                       if (count == max)
+                               bad_max = max;
+                       else if (count > max) {
+                               max = count;
+                               best = target_dir;
+                       }
+               }
+
+               if (bad_max == max) {
+                       path_msg(opt, source_dir, 0,
+                              _("CONFLICT (directory rename split): "
+                                "Unclear where to rename %s to; it was "
+                                "renamed to multiple other directories, with "
+                                "no destination getting a majority of the "
+                                "files."),
+                              source_dir);
+                       /*
+                        * We should mark this as unclean IF something attempts
+                        * to use this rename.  We do not yet have the logic
+                        * in place to detect if this directory rename is being
+                        * used, and optimizations that reduce the number of
+                        * renames cause this to falsely trigger.  For now,
+                        * just disable it, causing t6423 testcase 2a to break.
+                        * We'll later fix the detection, and when we do we
+                        * will re-enable setting *clean to 0 (and thereby fix
+                        * t6423 testcase 2a).
+                        */
+                       /*   *clean = 0;   */
+               } else {
+                       strmap_put(&renames->dir_renames[side],
+                                  source_dir, (void*)best);
+               }
+       }
+}
+
+static void handle_directory_level_conflicts(struct merge_options *opt)
+{
+       struct hashmap_iter iter;
+       struct strmap_entry *entry;
+       struct string_list duplicated = STRING_LIST_INIT_NODUP;
+       struct rename_info *renames = &opt->priv->renames;
+       struct strmap *side1_dir_renames = &renames->dir_renames[MERGE_SIDE1];
+       struct strmap *side2_dir_renames = &renames->dir_renames[MERGE_SIDE2];
+       int i;
+
+       strmap_for_each_entry(side1_dir_renames, &iter, entry) {
+               if (strmap_contains(side2_dir_renames, entry->key))
+                       string_list_append(&duplicated, entry->key);
+       }
+
+       for (i = 0; i < duplicated.nr; i++) {
+               strmap_remove(side1_dir_renames, duplicated.items[i].string, 0);
+               strmap_remove(side2_dir_renames, duplicated.items[i].string, 0);
+       }
+       string_list_clear(&duplicated, 0);
+}
+
+static struct strmap_entry *check_dir_renamed(const char *path,
+                                             struct strmap *dir_renames)
+{
+       char *temp = xstrdup(path);
+       char *end;
+       struct strmap_entry *e = NULL;
+
+       while ((end = strrchr(temp, '/'))) {
+               *end = '\0';
+               e = strmap_get_entry(dir_renames, temp);
+               if (e)
+                       break;
+       }
+       free(temp);
+       return e;
+}
+
+static void compute_collisions(struct strmap *collisions,
+                              struct strmap *dir_renames,
+                              struct diff_queue_struct *pairs)
+{
+       int i;
+
+       strmap_init_with_options(collisions, NULL, 0);
+       if (strmap_empty(dir_renames))
+               return;
+
+       /*
+        * Multiple files can be mapped to the same path due to directory
+        * renames done by the other side of history.  Since that other
+        * side of history could have merged multiple directories into one,
+        * if our side of history added the same file basename to each of
+        * those directories, then all N of them would get implicitly
+        * renamed by the directory rename detection into the same path,
+        * and we'd get an add/add/.../add conflict, and all those adds
+        * from *this* side of history.  This is not representable in the
+        * index, and users aren't going to easily be able to make sense of
+        * it.  So we need to provide a good warning about what's
+        * happening, and fall back to no-directory-rename detection
+        * behavior for those paths.
+        *
+        * See testcases 9e and all of section 5 from t6043 for examples.
+        */
+       for (i = 0; i < pairs->nr; ++i) {
+               struct strmap_entry *rename_info;
+               struct collision_info *collision_info;
+               char *new_path;
+               struct diff_filepair *pair = pairs->queue[i];
+
+               if (pair->status != 'A' && pair->status != 'R')
+                       continue;
+               rename_info = check_dir_renamed(pair->two->path, dir_renames);
+               if (!rename_info)
+                       continue;
+
+               new_path = apply_dir_rename(rename_info, pair->two->path);
+               assert(new_path);
+               collision_info = strmap_get(collisions, new_path);
+               if (collision_info) {
+                       free(new_path);
+               } else {
+                       collision_info = xcalloc(1,
+                                                sizeof(struct collision_info));
+                       string_list_init(&collision_info->source_files, 0);
+                       strmap_put(collisions, new_path, collision_info);
+               }
+               string_list_insert(&collision_info->source_files,
+                                  pair->two->path);
+       }
+}
+
+static char *check_for_directory_rename(struct merge_options *opt,
+                                       const char *path,
+                                       unsigned side_index,
+                                       struct strmap *dir_renames,
+                                       struct strmap *dir_rename_exclusions,
+                                       struct strmap *collisions,
+                                       int *clean_merge)
+{
+       char *new_path = NULL;
+       struct strmap_entry *rename_info;
+       struct strmap_entry *otherinfo = NULL;
+       const char *new_dir;
+
+       if (strmap_empty(dir_renames))
+               return new_path;
+       rename_info = check_dir_renamed(path, dir_renames);
+       if (!rename_info)
+               return new_path;
+       /* old_dir = rename_info->key; */
+       new_dir = rename_info->value;
+
+       /*
+        * This next part is a little weird.  We do not want to do an
+        * implicit rename into a directory we renamed on our side, because
+        * that will result in a spurious rename/rename(1to2) conflict.  An
+        * example:
+        *   Base commit: dumbdir/afile, otherdir/bfile
+        *   Side 1:      smrtdir/afile, otherdir/bfile
+        *   Side 2:      dumbdir/afile, dumbdir/bfile
+        * Here, while working on Side 1, we could notice that otherdir was
+        * renamed/merged to dumbdir, and change the diff_filepair for
+        * otherdir/bfile into a rename into dumbdir/bfile.  However, Side
+        * 2 will notice the rename from dumbdir to smrtdir, and do the
+        * transitive rename to move it from dumbdir/bfile to
+        * smrtdir/bfile.  That gives us bfile in dumbdir vs being in
+        * smrtdir, a rename/rename(1to2) conflict.  We really just want
+        * the file to end up in smrtdir.  And the way to achieve that is
+        * to not let Side1 do the rename to dumbdir, since we know that is
+        * the source of one of our directory renames.
+        *
+        * That's why otherinfo and dir_rename_exclusions is here.
+        *
+        * As it turns out, this also prevents N-way transient rename
+        * confusion; See testcases 9c and 9d of t6043.
+        */
+       otherinfo = strmap_get_entry(dir_rename_exclusions, new_dir);
+       if (otherinfo) {
+               path_msg(opt, rename_info->key, 1,
+                        _("WARNING: Avoiding applying %s -> %s rename "
+                          "to %s, because %s itself was renamed."),
+                        rename_info->key, new_dir, path, new_dir);
+               return NULL;
+       }
+
+       new_path = handle_path_level_conflicts(opt, path, side_index,
+                                              rename_info, collisions);
+       *clean_merge &= (new_path != NULL);
+
+       return new_path;
+}
+
+static void apply_directory_rename_modifications(struct merge_options *opt,
+                                                struct diff_filepair *pair,
+                                                char *new_path)
+{
+       /*
+        * The basic idea is to get the conflict_info from opt->priv->paths
+        * at old path, and insert it into new_path; basically just this:
+        *     ci = strmap_get(&opt->priv->paths, old_path);
+        *     strmap_remove(&opt->priv->paths, old_path, 0);
+        *     strmap_put(&opt->priv->paths, new_path, ci);
+        * However, there are some factors complicating this:
+        *     - opt->priv->paths may already have an entry at new_path
+        *     - Each ci tracks its containing directory, so we need to
+        *       update that
+        *     - If another ci has the same containing directory, then
+        *       the two char*'s MUST point to the same location.  See the
+        *       comment in struct merged_info.  strcmp equality is not
+        *       enough; we need pointer equality.
+        *     - opt->priv->paths must hold the parent directories of any
+        *       entries that are added.  So, if this directory rename
+        *       causes entirely new directories, we must recursively add
+        *       parent directories.
+        *     - For each parent directory added to opt->priv->paths, we
+        *       also need to get its parent directory stored in its
+        *       conflict_info->merged.directory_name with all the same
+        *       requirements about pointer equality.
+        */
+       struct string_list dirs_to_insert = STRING_LIST_INIT_NODUP;
+       struct conflict_info *ci, *new_ci;
+       struct strmap_entry *entry;
+       const char *branch_with_new_path, *branch_with_dir_rename;
+       const char *old_path = pair->two->path;
+       const char *parent_name;
+       const char *cur_path;
+       int i, len;
+
+       entry = strmap_get_entry(&opt->priv->paths, old_path);
+       old_path = entry->key;
+       ci = entry->value;
+       VERIFY_CI(ci);
+
+       /* Find parent directories missing from opt->priv->paths */
+       cur_path = new_path;
+       while (1) {
+               /* Find the parent directory of cur_path */
+               char *last_slash = strrchr(cur_path, '/');
+               if (last_slash) {
+                       parent_name = xstrndup(cur_path, last_slash - cur_path);
+               } else {
+                       parent_name = opt->priv->toplevel_dir;
+                       break;
+               }
+
+               /* Look it up in opt->priv->paths */
+               entry = strmap_get_entry(&opt->priv->paths, parent_name);
+               if (entry) {
+                       free((char*)parent_name);
+                       parent_name = entry->key; /* reuse known pointer */
+                       break;
+               }
+
+               /* Record this is one of the directories we need to insert */
+               string_list_append(&dirs_to_insert, parent_name);
+               cur_path = parent_name;
+       }
+
+       /* Traverse dirs_to_insert and insert them into opt->priv->paths */
+       for (i = dirs_to_insert.nr-1; i >= 0; --i) {
+               struct conflict_info *dir_ci;
+               char *cur_dir = dirs_to_insert.items[i].string;
+
+               dir_ci = xcalloc(1, sizeof(*dir_ci));
+
+               dir_ci->merged.directory_name = parent_name;
+               len = strlen(parent_name);
+               /* len+1 because of trailing '/' character */
+               dir_ci->merged.basename_offset = (len > 0 ? len+1 : len);
+               dir_ci->dirmask = ci->filemask;
+               strmap_put(&opt->priv->paths, cur_dir, dir_ci);
+
+               parent_name = cur_dir;
+       }
+
+       /*
+        * We are removing old_path from opt->priv->paths.  old_path also will
+        * eventually need to be freed, but it may still be used by e.g.
+        * ci->pathnames.  So, store it in another string-list for now.
+        */
+       string_list_append(&opt->priv->paths_to_free, old_path);
+
+       assert(ci->filemask == 2 || ci->filemask == 4);
+       assert(ci->dirmask == 0);
+       strmap_remove(&opt->priv->paths, old_path, 0);
+
+       branch_with_new_path   = (ci->filemask == 2) ? opt->branch1 : opt->branch2;
+       branch_with_dir_rename = (ci->filemask == 2) ? opt->branch2 : opt->branch1;
+
+       /* Now, finally update ci and stick it into opt->priv->paths */
+       ci->merged.directory_name = parent_name;
+       len = strlen(parent_name);
+       ci->merged.basename_offset = (len > 0 ? len+1 : len);
+       new_ci = strmap_get(&opt->priv->paths, new_path);
+       if (!new_ci) {
+               /* Place ci back into opt->priv->paths, but at new_path */
+               strmap_put(&opt->priv->paths, new_path, ci);
+       } else {
+               int index;
+
+               /* A few sanity checks */
+               VERIFY_CI(new_ci);
+               assert(ci->filemask == 2 || ci->filemask == 4);
+               assert((new_ci->filemask & ci->filemask) == 0);
+               assert(!new_ci->merged.clean);
+
+               /* Copy stuff from ci into new_ci */
+               new_ci->filemask |= ci->filemask;
+               if (new_ci->dirmask)
+                       new_ci->df_conflict = 1;
+               index = (ci->filemask >> 1);
+               new_ci->pathnames[index] = ci->pathnames[index];
+               new_ci->stages[index].mode = ci->stages[index].mode;
+               oidcpy(&new_ci->stages[index].oid, &ci->stages[index].oid);
+
+               free(ci);
+               ci = new_ci;
+       }
+
+       if (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE) {
+               /* Notify user of updated path */
+               if (pair->status == 'A')
+                       path_msg(opt, new_path, 1,
+                                _("Path updated: %s added in %s inside a "
+                                  "directory that was renamed in %s; moving "
+                                  "it to %s."),
+                                old_path, branch_with_new_path,
+                                branch_with_dir_rename, new_path);
+               else
+                       path_msg(opt, new_path, 1,
+                                _("Path updated: %s renamed to %s in %s, "
+                                  "inside a directory that was renamed in %s; "
+                                  "moving it to %s."),
+                                pair->one->path, old_path, branch_with_new_path,
+                                branch_with_dir_rename, new_path);
+       } else {
+               /*
+                * opt->detect_directory_renames has the value
+                * MERGE_DIRECTORY_RENAMES_CONFLICT, so mark these as conflicts.
+                */
+               ci->path_conflict = 1;
+               if (pair->status == 'A')
+                       path_msg(opt, new_path, 0,
+                                _("CONFLICT (file location): %s added in %s "
+                                  "inside a directory that was renamed in %s, "
+                                  "suggesting it should perhaps be moved to "
+                                  "%s."),
+                                old_path, branch_with_new_path,
+                                branch_with_dir_rename, new_path);
+               else
+                       path_msg(opt, new_path, 0,
+                                _("CONFLICT (file location): %s renamed to %s "
+                                  "in %s, inside a directory that was renamed "
+                                  "in %s, suggesting it should perhaps be "
+                                  "moved to %s."),
+                                pair->one->path, old_path, branch_with_new_path,
+                                branch_with_dir_rename, new_path);
+       }
+
+       /*
+        * Finally, record the new location.
+        */
+       pair->two->path = new_path;
+}
+
 /*** Function Grouping: functions related to regular rename detection ***/
 
 static int process_renames(struct merge_options *opt,
@@ -1081,12 +1805,28 @@ static int process_renames(struct merge_options *opt,
                const char *rename_branch = NULL, *delete_branch = NULL;
 
                old_ent = strmap_get_entry(&opt->priv->paths, pair->one->path);
-               oldpath = old_ent->key;
-               oldinfo = old_ent->value;
-
                new_ent = strmap_get_entry(&opt->priv->paths, pair->two->path);
-               newpath = new_ent->key;
-               newinfo = new_ent->value;
+               if (old_ent) {
+                       oldpath = old_ent->key;
+                       oldinfo = old_ent->value;
+               }
+               newpath = pair->two->path;
+               if (new_ent) {
+                       newpath = new_ent->key;
+                       newinfo = new_ent->value;
+               }
+
+               /*
+                * If pair->one->path isn't in opt->priv->paths, that means
+                * that either directory rename detection removed that
+                * path, or a parent directory of oldpath was resolved and
+                * we don't even need the rename; in either case, we can
+                * skip it.  If oldinfo->merged.clean, then the other side
+                * of history had no changes to oldpath and we don't need
+                * the rename and can skip it.
+                */
+               if (!oldinfo || oldinfo->merged.clean)
+                       continue;
 
                /*
                 * diff_filepairs have copies of pathnames, thus we have to
@@ -1367,9 +2107,12 @@ static void detect_regular_renames(struct merge_options *opt,
        diff_opts.show_rename_progress = opt->show_rename_progress;
        diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&diff_opts);
+
+       trace2_region_enter("diff", "diffcore_rename", opt->repo);
        diff_tree_oid(&merge_base->object.oid, &side->object.oid, "",
                      &diff_opts);
        diffcore_std(&diff_opts);
+       trace2_region_leave("diff", "diffcore_rename", opt->repo);
 
        if (diff_opts.needed_rename_limit > renames->needed_limit)
                renames->needed_limit = diff_opts.needed_rename_limit;
@@ -1388,22 +2131,44 @@ static void detect_regular_renames(struct merge_options *opt,
  */
 static int collect_renames(struct merge_options *opt,
                           struct diff_queue_struct *result,
-                          unsigned side_index)
+                          unsigned side_index,
+                          struct strmap *dir_renames_for_side,
+                          struct strmap *rename_exclusions)
 {
        int i, clean = 1;
+       struct strmap collisions;
        struct diff_queue_struct *side_pairs;
+       struct hashmap_iter iter;
+       struct strmap_entry *entry;
        struct rename_info *renames = &opt->priv->renames;
 
        side_pairs = &renames->pairs[side_index];
+       compute_collisions(&collisions, dir_renames_for_side, side_pairs);
 
        for (i = 0; i < side_pairs->nr; ++i) {
                struct diff_filepair *p = side_pairs->queue[i];
+               char *new_path; /* non-NULL only with directory renames */
 
-               if (p->status != 'R') {
+               if (p->status != 'A' && p->status != 'R') {
                        diff_free_filepair(p);
                        continue;
                }
 
+               new_path = check_for_directory_rename(opt, p->two->path,
+                                                     side_index,
+                                                     dir_renames_for_side,
+                                                     rename_exclusions,
+                                                     &collisions,
+                                                     &clean);
+
+               if (p->status != 'R' && !new_path) {
+                       diff_free_filepair(p);
+                       continue;
+               }
+
+               if (new_path)
+                       apply_directory_rename_modifications(opt, p, new_path);
+
                /*
                 * p->score comes back from diffcore_rename_extended() with
                 * the similarity of the renamed file.  The similarity is
@@ -1418,6 +2183,20 @@ static int collect_renames(struct merge_options *opt,
                result->queue[result->nr++] = p;
        }
 
+       /* Free each value in the collisions map */
+       strmap_for_each_entry(&collisions, &iter, entry) {
+               struct collision_info *info = entry->value;
+               string_list_clear(&info->source_files, 0);
+       }
+       /*
+        * In compute_collisions(), we set collisions.strdup_strings to 0
+        * so that we wouldn't have to make another copy of the new_path
+        * allocated by apply_dir_rename().  But now that we've used them
+        * and have no other references to these strings, it is time to
+        * deallocate them.
+        */
+       free_strmap_strings(&collisions);
+       strmap_clear(&collisions, 1);
        return clean;
 }
 
@@ -1428,21 +2207,42 @@ static int detect_and_process_renames(struct merge_options *opt,
 {
        struct diff_queue_struct combined;
        struct rename_info *renames = &opt->priv->renames;
-       int s, clean = 1;
+       int need_dir_renames, s, clean = 1;
 
        memset(&combined, 0, sizeof(combined));
 
+       trace2_region_enter("merge", "regular renames", opt->repo);
        detect_regular_renames(opt, merge_base, side1, MERGE_SIDE1);
        detect_regular_renames(opt, merge_base, side2, MERGE_SIDE2);
+       trace2_region_leave("merge", "regular renames", opt->repo);
+
+       trace2_region_enter("merge", "directory renames", opt->repo);
+       need_dir_renames =
+         !opt->priv->call_depth &&
+         (opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_TRUE ||
+          opt->detect_directory_renames == MERGE_DIRECTORY_RENAMES_CONFLICT);
+
+       if (need_dir_renames) {
+               get_provisional_directory_renames(opt, MERGE_SIDE1, &clean);
+               get_provisional_directory_renames(opt, MERGE_SIDE2, &clean);
+               handle_directory_level_conflicts(opt);
+       }
 
        ALLOC_GROW(combined.queue,
                   renames->pairs[1].nr + renames->pairs[2].nr,
                   combined.alloc);
-       clean &= collect_renames(opt, &combined, MERGE_SIDE1);
-       clean &= collect_renames(opt, &combined, MERGE_SIDE2);
+       clean &= collect_renames(opt, &combined, MERGE_SIDE1,
+                                &renames->dir_renames[2],
+                                &renames->dir_renames[1]);
+       clean &= collect_renames(opt, &combined, MERGE_SIDE2,
+                                &renames->dir_renames[1],
+                                &renames->dir_renames[2]);
        QSORT(combined.queue, combined.nr, compare_pairs);
+       trace2_region_leave("merge", "directory renames", opt->repo);
 
+       trace2_region_enter("merge", "process renames", opt->repo);
        clean &= process_renames(opt, &combined);
+       trace2_region_leave("merge", "process renames", opt->repo);
 
        /* Free memory for renames->pairs[] and combined */
        for (s = MERGE_SIDE1; s <= MERGE_SIDE2; s++) {
@@ -2124,20 +2924,30 @@ static void process_entries(struct merge_options *opt,
                                                   STRING_LIST_INIT_NODUP,
                                                   NULL, 0 };
 
+       trace2_region_enter("merge", "process_entries setup", opt->repo);
        if (strmap_empty(&opt->priv->paths)) {
                oidcpy(result_oid, opt->repo->hash_algo->empty_tree);
                return;
        }
 
        /* Hack to pre-allocate plist to the desired size */
+       trace2_region_enter("merge", "plist grow", opt->repo);
        ALLOC_GROW(plist.items, strmap_get_size(&opt->priv->paths), plist.alloc);
+       trace2_region_leave("merge", "plist grow", opt->repo);
 
        /* Put every entry from paths into plist, then sort */
+       trace2_region_enter("merge", "plist copy", opt->repo);
        strmap_for_each_entry(&opt->priv->paths, &iter, e) {
                string_list_append(&plist, e->key)->util = e->value;
        }
+       trace2_region_leave("merge", "plist copy", opt->repo);
+
+       trace2_region_enter("merge", "plist special sort", opt->repo);
        plist.cmp = string_list_df_name_compare;
        string_list_sort(&plist);
+       trace2_region_leave("merge", "plist special sort", opt->repo);
+
+       trace2_region_leave("merge", "process_entries setup", opt->repo);
 
        /*
         * Iterate over the items in reverse order, so we can handle paths
@@ -2148,6 +2958,7 @@ static void process_entries(struct merge_options *opt,
         * (because it allows us to know whether the directory is still in
         * the way when it is time to process the file at the same path).
         */
+       trace2_region_enter("merge", "processing", opt->repo);
        for (entry = &plist.items[plist.nr-1]; entry >= plist.items; --entry) {
                char *path = entry->string;
                /*
@@ -2166,7 +2977,9 @@ static void process_entries(struct merge_options *opt,
                        process_entry(opt, path, ci, &dir_metadata);
                }
        }
+       trace2_region_leave("merge", "processing", opt->repo);
 
+       trace2_region_enter("merge", "process_entries cleanup", opt->repo);
        if (dir_metadata.offsets.nr != 1 ||
            (uintptr_t)dir_metadata.offsets.items[0].util != 0) {
                printf("dir_metadata.offsets.nr = %d (should be 1)\n",
@@ -2181,6 +2994,7 @@ static void process_entries(struct merge_options *opt,
        string_list_clear(&plist, 0);
        string_list_clear(&dir_metadata.versions, 0);
        string_list_clear(&dir_metadata.offsets, 0);
+       trace2_region_leave("merge", "process_entries cleanup", opt->repo);
 }
 
 /*** Function Grouping: functions related to merge_switch_to_result() ***/
@@ -2339,12 +3153,15 @@ void merge_switch_to_result(struct merge_options *opt,
        if (result->clean >= 0 && update_worktree_and_index) {
                struct merge_options_internal *opti = result->priv;
 
+               trace2_region_enter("merge", "checkout", opt->repo);
                if (checkout(opt, head, result->tree)) {
                        /* failure to function */
                        result->clean = -1;
                        return;
                }
+               trace2_region_leave("merge", "checkout", opt->repo);
 
+               trace2_region_enter("merge", "record_conflicted", opt->repo);
                if (record_conflicted_index_entries(opt, opt->repo->index,
                                                    &opti->paths,
                                                    &opti->conflicted)) {
@@ -2352,6 +3169,7 @@ void merge_switch_to_result(struct merge_options *opt,
                        result->clean = -1;
                        return;
                }
+               trace2_region_leave("merge", "record_conflicted", opt->repo);
        }
 
        if (display_update_msgs) {
@@ -2361,6 +3179,8 @@ void merge_switch_to_result(struct merge_options *opt,
                struct string_list olist = STRING_LIST_INIT_NODUP;
                int i;
 
+               trace2_region_enter("merge", "display messages", opt->repo);
+
                /* Hack to pre-allocate olist to the desired size */
                ALLOC_GROW(olist.items, strmap_get_size(&opti->output),
                           olist.alloc);
@@ -2382,6 +3202,8 @@ void merge_switch_to_result(struct merge_options *opt,
                /* Also include needed rename limit adjustment now */
                diff_warn_rename_limit("merge.renamelimit",
                                       opti->renames.needed_limit, 0);
+
+               trace2_region_leave("merge", "display messages", opt->repo);
        }
 
        merge_finalize(opt, result);
@@ -2419,7 +3241,11 @@ static struct commit *make_virtual_commit(struct repository *repo,
 
 static void merge_start(struct merge_options *opt, struct merge_result *result)
 {
+       struct rename_info *renames;
+       int i;
+
        /* Sanity checks on opt */
+       trace2_region_enter("merge", "sanity checks", opt->repo);
        assert(opt->repo);
 
        assert(opt->branch1 && opt->branch2);
@@ -2446,13 +3272,43 @@ static void merge_start(struct merge_options *opt, struct merge_result *result)
        assert(opt->obuf.len == 0);
 
        assert(opt->priv == NULL);
+       if (result->priv) {
+               opt->priv = result->priv;
+               result->priv = NULL;
+               /*
+                * opt->priv non-NULL means we had results from a previous
+                * run; do a few sanity checks that user didn't mess with
+                * it in an obvious fashion.
+                */
+               assert(opt->priv->call_depth == 0);
+               assert(!opt->priv->toplevel_dir ||
+                      0 == strlen(opt->priv->toplevel_dir));
+       }
+       trace2_region_leave("merge", "sanity checks", opt->repo);
 
        /* Default to histogram diff.  Actually, just hardcode it...for now. */
        opt->xdl_opts = DIFF_WITH_ALG(opt, HISTOGRAM_DIFF);
 
        /* Initialization of opt->priv, our internal merge data */
+       trace2_region_enter("merge", "allocate/init", opt->repo);
+       if (opt->priv) {
+               clear_or_reinit_internal_opts(opt->priv, 1);
+               trace2_region_leave("merge", "allocate/init", opt->repo);
+               return;
+       }
        opt->priv = xcalloc(1, sizeof(*opt->priv));
 
+       /* Initialization of various renames fields */
+       renames = &opt->priv->renames;
+       for (i = MERGE_SIDE1; i <= MERGE_SIDE2; i++) {
+               strset_init_with_options(&renames->dirs_removed[i],
+                                        NULL, 0);
+               strmap_init_with_options(&renames->dir_rename_count[i],
+                                        NULL, 1);
+               strmap_init_with_options(&renames->dir_renames[i],
+                                        NULL, 0);
+       }
+
        /*
         * Although we initialize opt->priv->paths with strdup_strings=0,
         * that's just to avoid making yet another copy of an allocated
@@ -2472,6 +3328,8 @@ static void merge_start(struct merge_options *opt, struct merge_result *result)
         * subset of the overall paths that have special output.
         */
        strmap_init(&opt->priv->output);
+
+       trace2_region_leave("merge", "allocate/init", opt->repo);
 }
 
 /*** Function Grouping: merge_incore_*() and their internal variants ***/
@@ -2487,6 +3345,7 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt,
 {
        struct object_id working_tree_oid;
 
+       trace2_region_enter("merge", "collect_merge_info", opt->repo);
        if (collect_merge_info(opt, merge_base, side1, side2) != 0) {
                /*
                 * TRANSLATORS: The %s arguments are: 1) tree hash of a merge
@@ -2499,10 +3358,16 @@ static void merge_ort_nonrecursive_internal(struct merge_options *opt,
                result->clean = -1;
                return;
        }
+       trace2_region_leave("merge", "collect_merge_info", opt->repo);
 
+       trace2_region_enter("merge", "renames", opt->repo);
        result->clean = detect_and_process_renames(opt, merge_base,
                                                   side1, side2);
+       trace2_region_leave("merge", "renames", opt->repo);
+
+       trace2_region_enter("merge", "process_entries", opt->repo);
        process_entries(opt, &working_tree_oid);
+       trace2_region_leave("merge", "process_entries", opt->repo);
 
        /* Set return values */
        result->tree = parse_tree_indirect(&working_tree_oid);
@@ -2603,9 +3468,15 @@ void merge_incore_nonrecursive(struct merge_options *opt,
                               struct tree *side2,
                               struct merge_result *result)
 {
+       trace2_region_enter("merge", "incore_nonrecursive", opt->repo);
+
+       trace2_region_enter("merge", "merge_start", opt->repo);
        assert(opt->ancestor != NULL);
        merge_start(opt, result);
+       trace2_region_leave("merge", "merge_start", opt->repo);
+
        merge_ort_nonrecursive_internal(opt, merge_base, side1, side2, result);
+       trace2_region_leave("merge", "incore_nonrecursive", opt->repo);
 }
 
 void merge_incore_recursive(struct merge_options *opt,
@@ -2614,9 +3485,15 @@ void merge_incore_recursive(struct merge_options *opt,
                            struct commit *side2,
                            struct merge_result *result)
 {
+       trace2_region_enter("merge", "incore_recursive", opt->repo);
+
        /* We set the ancestor label based on the merge_bases */
        assert(opt->ancestor == NULL);
 
+       trace2_region_enter("merge", "merge_start", opt->repo);
        merge_start(opt, result);
+       trace2_region_leave("merge", "merge_start", opt->repo);
+
        merge_ort_internal(opt, merge_bases, side1, side2, result);
+       trace2_region_leave("merge", "incore_recursive", opt->repo);
 }
index c4fc9dd74ea81c20830ec1b79788a69faaccaf9a..541dab0858623b6cd5f0f81abd7ff36508bf2acf 100644 (file)
@@ -85,6 +85,9 @@ struct packed_git {
                 multi_pack_index:1;
        unsigned char hash[GIT_MAX_RAWSZ];
        struct revindex_entry *revindex;
+       const uint32_t *revindex_data;
+       const uint32_t *revindex_map;
+       size_t revindex_size;
        /* something like ".git/objects/pack/xxxxx.pack" */
        char pack_name[FLEX_ARRAY]; /* more */
 };
index 60fe20fb87a42ea05e3cd16f07966bf30448ae3f..1f69b5fa85354b76169f93139a29e7d98846795f 100644 (file)
@@ -1430,3 +1430,84 @@ int bitmap_has_oid_in_uninteresting(struct bitmap_index *bitmap_git,
        return bitmap_git &&
                bitmap_walk_contains(bitmap_git, bitmap_git->haves, oid);
 }
+
+static off_t get_disk_usage_for_type(struct bitmap_index *bitmap_git,
+                                    enum object_type object_type)
+{
+       struct bitmap *result = bitmap_git->result;
+       struct packed_git *pack = bitmap_git->pack;
+       off_t total = 0;
+       struct ewah_iterator it;
+       eword_t filter;
+       size_t i;
+
+       init_type_iterator(&it, bitmap_git, object_type);
+       for (i = 0; i < result->word_alloc &&
+                       ewah_iterator_next(&filter, &it); i++) {
+               eword_t word = result->words[i] & filter;
+               size_t base = (i * BITS_IN_EWORD);
+               unsigned offset;
+
+               if (!word)
+                       continue;
+
+               for (offset = 0; offset < BITS_IN_EWORD; offset++) {
+                       size_t pos;
+
+                       if ((word >> offset) == 0)
+                               break;
+
+                       offset += ewah_bit_ctz64(word >> offset);
+                       pos = base + offset;
+                       total += pack_pos_to_offset(pack, pos + 1) -
+                                pack_pos_to_offset(pack, pos);
+               }
+       }
+
+       return total;
+}
+
+static off_t get_disk_usage_for_extended(struct bitmap_index *bitmap_git)
+{
+       struct bitmap *result = bitmap_git->result;
+       struct packed_git *pack = bitmap_git->pack;
+       struct eindex *eindex = &bitmap_git->ext_index;
+       off_t total = 0;
+       struct object_info oi = OBJECT_INFO_INIT;
+       off_t object_size;
+       size_t i;
+
+       oi.disk_sizep = &object_size;
+
+       for (i = 0; i < eindex->count; i++) {
+               struct object *obj = eindex->objects[i];
+
+               if (!bitmap_get(result, pack->num_objects + i))
+                       continue;
+
+               if (oid_object_info_extended(the_repository, &obj->oid, &oi, 0) < 0)
+                       die(_("unable to get disk usage of %s"),
+                           oid_to_hex(&obj->oid));
+
+               total += object_size;
+       }
+       return total;
+}
+
+off_t get_disk_usage_from_bitmap(struct bitmap_index *bitmap_git,
+                                struct rev_info *revs)
+{
+       off_t total = 0;
+
+       total += get_disk_usage_for_type(bitmap_git, OBJ_COMMIT);
+       if (revs->tree_objects)
+               total += get_disk_usage_for_type(bitmap_git, OBJ_TREE);
+       if (revs->blob_objects)
+               total += get_disk_usage_for_type(bitmap_git, OBJ_BLOB);
+       if (revs->tag_objects)
+               total += get_disk_usage_for_type(bitmap_git, OBJ_TAG);
+
+       total += get_disk_usage_for_extended(bitmap_git);
+
+       return total;
+}
index 25dfcf56156bf8b9b03ebbbaae895a71fc8cf89b..36d99930d8d5288a7367235eba8d533ed8f76f99 100644 (file)
@@ -68,6 +68,8 @@ int bitmap_walk_contains(struct bitmap_index *,
  */
 int bitmap_has_oid_in_uninteresting(struct bitmap_index *, const struct object_id *oid);
 
+off_t get_disk_usage_from_bitmap(struct bitmap_index *, struct rev_info *);
+
 void bitmap_writer_show_progress(int show);
 void bitmap_writer_set_checksum(unsigned char *sha1);
 void bitmap_writer_build_type_index(struct packing_data *to_pack,
index 5e69bc7372f5476429fb11c53d5b4bd24390798c..83fe4de77382a1089b62972e3fa5d7771c1a0790 100644 (file)
@@ -2,6 +2,7 @@
 #include "pack-revindex.h"
 #include "object-store.h"
 #include "packfile.h"
+#include "config.h"
 
 struct revindex_entry {
        off_t offset;
@@ -164,16 +165,133 @@ static void create_pack_revindex(struct packed_git *p)
        sort_revindex(p->revindex, num_ent, p->pack_size);
 }
 
-int load_pack_revindex(struct packed_git *p)
+static int create_pack_revindex_in_memory(struct packed_git *p)
 {
-       if (!p->revindex) {
-               if (open_pack_index(p))
-                       return -1;
-               create_pack_revindex(p);
-       }
+       if (git_env_bool(GIT_TEST_REV_INDEX_DIE_IN_MEMORY, 0))
+               die("dying as requested by '%s'",
+                   GIT_TEST_REV_INDEX_DIE_IN_MEMORY);
+       if (open_pack_index(p))
+               return -1;
+       create_pack_revindex(p);
        return 0;
 }
 
+static char *pack_revindex_filename(struct packed_git *p)
+{
+       size_t len;
+       if (!strip_suffix(p->pack_name, ".pack", &len))
+               BUG("pack_name does not end in .pack");
+       return xstrfmt("%.*s.rev", (int)len, p->pack_name);
+}
+
+#define RIDX_HEADER_SIZE (12)
+#define RIDX_MIN_SIZE (RIDX_HEADER_SIZE + (2 * the_hash_algo->rawsz))
+
+struct revindex_header {
+       uint32_t signature;
+       uint32_t version;
+       uint32_t hash_id;
+};
+
+static int load_revindex_from_disk(char *revindex_name,
+                                  uint32_t num_objects,
+                                  const uint32_t **data_p, size_t *len_p)
+{
+       int fd, ret = 0;
+       struct stat st;
+       void *data = NULL;
+       size_t revindex_size;
+       struct revindex_header *hdr;
+
+       fd = git_open(revindex_name);
+
+       if (fd < 0) {
+               ret = -1;
+               goto cleanup;
+       }
+       if (fstat(fd, &st)) {
+               ret = error_errno(_("failed to read %s"), revindex_name);
+               goto cleanup;
+       }
+
+       revindex_size = xsize_t(st.st_size);
+
+       if (revindex_size < RIDX_MIN_SIZE) {
+               ret = error(_("reverse-index file %s is too small"), revindex_name);
+               goto cleanup;
+       }
+
+       if (revindex_size - RIDX_MIN_SIZE != st_mult(sizeof(uint32_t), num_objects)) {
+               ret = error(_("reverse-index file %s is corrupt"), revindex_name);
+               goto cleanup;
+       }
+
+       data = xmmap(NULL, revindex_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       hdr = data;
+
+       if (ntohl(hdr->signature) != RIDX_SIGNATURE) {
+               ret = error(_("reverse-index file %s has unknown signature"), revindex_name);
+               goto cleanup;
+       }
+       if (ntohl(hdr->version) != 1) {
+               ret = error(_("reverse-index file %s has unsupported version %"PRIu32),
+                           revindex_name, ntohl(hdr->version));
+               goto cleanup;
+       }
+       if (!(ntohl(hdr->hash_id) == 1 || ntohl(hdr->hash_id) == 2)) {
+               ret = error(_("reverse-index file %s has unsupported hash id %"PRIu32),
+                           revindex_name, ntohl(hdr->hash_id));
+               goto cleanup;
+       }
+
+cleanup:
+       if (ret) {
+               if (data)
+                       munmap(data, revindex_size);
+       } else {
+               *len_p = revindex_size;
+               *data_p = (const uint32_t *)data;
+       }
+
+       close(fd);
+       return ret;
+}
+
+static int load_pack_revindex_from_disk(struct packed_git *p)
+{
+       char *revindex_name;
+       int ret;
+       if (open_pack_index(p))
+               return -1;
+
+       revindex_name = pack_revindex_filename(p);
+
+       ret = load_revindex_from_disk(revindex_name,
+                                     p->num_objects,
+                                     &p->revindex_map,
+                                     &p->revindex_size);
+       if (ret)
+               goto cleanup;
+
+       p->revindex_data = (const uint32_t *)((const char *)p->revindex_map + RIDX_HEADER_SIZE);
+
+cleanup:
+       free(revindex_name);
+       return ret;
+}
+
+int load_pack_revindex(struct packed_git *p)
+{
+       if (p->revindex || p->revindex_data)
+               return 0;
+
+       if (!load_pack_revindex_from_disk(p))
+               return 0;
+       else if (!create_pack_revindex_in_memory(p))
+               return 0;
+       return -1;
+}
+
 int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
 {
        unsigned lo, hi;
@@ -203,18 +321,28 @@ int offset_to_pack_pos(struct packed_git *p, off_t ofs, uint32_t *pos)
 
 uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos)
 {
-       if (!p->revindex)
+       if (!(p->revindex || p->revindex_data))
                BUG("pack_pos_to_index: reverse index not yet loaded");
        if (p->num_objects <= pos)
                BUG("pack_pos_to_index: out-of-bounds object at %"PRIu32, pos);
-       return p->revindex[pos].nr;
+
+       if (p->revindex)
+               return p->revindex[pos].nr;
+       else
+               return get_be32(p->revindex_data + pos);
 }
 
 off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos)
 {
-       if (!p->revindex)
+       if (!(p->revindex || p->revindex_data))
                BUG("pack_pos_to_index: reverse index not yet loaded");
        if (p->num_objects < pos)
                BUG("pack_pos_to_offset: out-of-bounds object at %"PRIu32, pos);
-       return p->revindex[pos].offset;
+
+       if (p->revindex)
+               return p->revindex[pos].offset;
+       else if (pos == p->num_objects)
+               return p->pack_size - the_hash_algo->rawsz;
+       else
+               return nth_packed_object_offset(p, pack_pos_to_index(p, pos));
 }
index 6e0320b08b54aee2d636f87d6cd1af304d7fe58b..ba7c82c1251489a90480d4a6e5d6996e7e8226e6 100644 (file)
  *   can be found
  */
 
+
+#define RIDX_SIGNATURE 0x52494458 /* "RIDX" */
+#define RIDX_VERSION 1
+
+#define GIT_TEST_WRITE_REV_INDEX "GIT_TEST_WRITE_REV_INDEX"
+#define GIT_TEST_REV_INDEX_DIE_IN_MEMORY "GIT_TEST_REV_INDEX_DIE_IN_MEMORY"
+
 struct packed_git;
 
 /*
  * load_pack_revindex populates the revindex's internal data-structures for the
  * given pack, returning zero on success and a negative value otherwise.
+ *
+ * If a '.rev' file is present it is mmap'd, and pointers are assigned into it
+ * (instead of using the in-memory variant).
  */
 int load_pack_revindex(struct packed_git *p);
 
@@ -55,7 +65,9 @@ uint32_t pack_pos_to_index(struct packed_git *p, uint32_t pos);
  * If the reverse index has not yet been loaded, or the position is out of
  * bounds, this function aborts.
  *
- * This function runs in constant time.
+ * This function runs in constant time under both in-memory and on-disk reverse
+ * indexes, but an additional step is taken to consult the corresponding .idx
+ * file when using the on-disk format.
  */
 off_t pack_pos_to_offset(struct packed_git *p, uint32_t pos);
 
index e9bb3fd949b36d1ea1e5a61fb6d08592047223ff..680c36755dd2f112fefb20c9b86f59f36e4c59c5 100644 (file)
@@ -167,6 +167,113 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
        return index_name;
 }
 
+static int pack_order_cmp(const void *va, const void *vb, void *ctx)
+{
+       struct pack_idx_entry **objects = ctx;
+
+       off_t oa = objects[*(uint32_t*)va]->offset;
+       off_t ob = objects[*(uint32_t*)vb]->offset;
+
+       if (oa < ob)
+               return -1;
+       if (oa > ob)
+               return 1;
+       return 0;
+}
+
+static void write_rev_header(struct hashfile *f)
+{
+       uint32_t oid_version;
+       switch (hash_algo_by_ptr(the_hash_algo)) {
+       case GIT_HASH_SHA1:
+               oid_version = 1;
+               break;
+       case GIT_HASH_SHA256:
+               oid_version = 2;
+               break;
+       default:
+               die("write_rev_header: unknown hash version");
+       }
+
+       hashwrite_be32(f, RIDX_SIGNATURE);
+       hashwrite_be32(f, RIDX_VERSION);
+       hashwrite_be32(f, oid_version);
+}
+
+static void write_rev_index_positions(struct hashfile *f,
+                                     struct pack_idx_entry **objects,
+                                     uint32_t nr_objects)
+{
+       uint32_t *pack_order;
+       uint32_t i;
+
+       ALLOC_ARRAY(pack_order, nr_objects);
+       for (i = 0; i < nr_objects; i++)
+               pack_order[i] = i;
+       QSORT_S(pack_order, nr_objects, pack_order_cmp, objects);
+
+       for (i = 0; i < nr_objects; i++)
+               hashwrite_be32(f, pack_order[i]);
+
+       free(pack_order);
+}
+
+static void write_rev_trailer(struct hashfile *f, const unsigned char *hash)
+{
+       hashwrite(f, hash, the_hash_algo->rawsz);
+}
+
+const char *write_rev_file(const char *rev_name,
+                          struct pack_idx_entry **objects,
+                          uint32_t nr_objects,
+                          const unsigned char *hash,
+                          unsigned flags)
+{
+       struct hashfile *f;
+       int fd;
+
+       if ((flags & WRITE_REV) && (flags & WRITE_REV_VERIFY))
+               die(_("cannot both write and verify reverse index"));
+
+       if (flags & WRITE_REV) {
+               if (!rev_name) {
+                       struct strbuf tmp_file = STRBUF_INIT;
+                       fd = odb_mkstemp(&tmp_file, "pack/tmp_rev_XXXXXX");
+                       rev_name = strbuf_detach(&tmp_file, NULL);
+               } else {
+                       unlink(rev_name);
+                       fd = open(rev_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+                       if (fd < 0)
+                               die_errno("unable to create '%s'", rev_name);
+               }
+               f = hashfd(fd, rev_name);
+       } else if (flags & WRITE_REV_VERIFY) {
+               struct stat statbuf;
+               if (stat(rev_name, &statbuf)) {
+                       if (errno == ENOENT) {
+                               /* .rev files are optional */
+                               return NULL;
+                       } else
+                               die_errno(_("could not stat: %s"), rev_name);
+               }
+               f = hashfd_check(rev_name);
+       } else
+               return NULL;
+
+       write_rev_header(f);
+
+       write_rev_index_positions(f, objects, nr_objects);
+       write_rev_trailer(f, hash);
+
+       if (rev_name && adjust_shared_perm(rev_name) < 0)
+               die(_("failed to make %s readable"), rev_name);
+
+       finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_CLOSE |
+                                   ((flags & WRITE_IDX_VERIFY) ? 0 : CSUM_FSYNC));
+
+       return rev_name;
+}
+
 off_t write_pack_header(struct hashfile *f, uint32_t nr_entries)
 {
        struct pack_header hdr;
@@ -342,7 +449,7 @@ void finish_tmp_packfile(struct strbuf *name_buffer,
                         struct pack_idx_option *pack_idx_opts,
                         unsigned char hash[])
 {
-       const char *idx_tmp_name;
+       const char *idx_tmp_name, *rev_tmp_name = NULL;
        int basename_len = name_buffer->len;
 
        if (adjust_shared_perm(pack_tmp_name))
@@ -353,6 +460,9 @@ void finish_tmp_packfile(struct strbuf *name_buffer,
        if (adjust_shared_perm(idx_tmp_name))
                die_errno("unable to make temporary index file readable");
 
+       rev_tmp_name = write_rev_file(NULL, written_list, nr_written, hash,
+                                     pack_idx_opts->flags);
+
        strbuf_addf(name_buffer, "%s.pack", hash_to_hex(hash));
 
        if (rename(pack_tmp_name, name_buffer->buf))
@@ -366,6 +476,14 @@ void finish_tmp_packfile(struct strbuf *name_buffer,
 
        strbuf_setlen(name_buffer, basename_len);
 
+       if (rev_tmp_name) {
+               strbuf_addf(name_buffer, "%s.rev", hash_to_hex(hash));
+               if (rename(rev_tmp_name, name_buffer->buf))
+                       die_errno("unable to rename temporary reverse-index file");
+       }
+
+       strbuf_setlen(name_buffer, basename_len);
+
        free((void *)idx_tmp_name);
 }
 
diff --git a/pack.h b/pack.h
index 9ae640f4176f88836a21ff69416f5edef2bc5e9f..afdcf8f5c74df66eb3ea2dc10bf825d1b53ec17b 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -42,6 +42,8 @@ struct pack_idx_option {
        /* flag bits */
 #define WRITE_IDX_VERIFY 01 /* verify only, do not write the idx file */
 #define WRITE_IDX_STRICT 02
+#define WRITE_REV 04
+#define WRITE_REV_VERIFY 010
 
        uint32_t version;
        uint32_t off32_limit;
@@ -91,6 +93,8 @@ struct ref;
 
 void write_promisor_file(const char *promisor_name, struct ref **sought, int nr_sought);
 
+const char *write_rev_file(const char *rev_name, struct pack_idx_entry **objects, uint32_t nr_objects, const unsigned char *hash, unsigned flags);
+
 /*
  * The "hdr" output buffer should be at least this big, which will handle sizes
  * up to 2^67.
index 4b938b4372617c4674d2ef12589d2b860fd1f007..1fec12ac5f4c74448d5bf58699d9a05f654ef3b6 100644 (file)
@@ -324,11 +324,21 @@ void close_pack_index(struct packed_git *p)
        }
 }
 
+void close_pack_revindex(struct packed_git *p) {
+       if (!p->revindex_map)
+               return;
+
+       munmap((void *)p->revindex_map, p->revindex_size);
+       p->revindex_map = NULL;
+       p->revindex_data = NULL;
+}
+
 void close_pack(struct packed_git *p)
 {
        close_pack_windows(p);
        close_pack_fd(p);
        close_pack_index(p);
+       close_pack_revindex(p);
 }
 
 void close_object_store(struct raw_object_store *o)
@@ -351,7 +361,7 @@ void close_object_store(struct raw_object_store *o)
 
 void unlink_pack_path(const char *pack_name, int force_delete)
 {
-       static const char *exts[] = {".pack", ".idx", ".keep", ".bitmap", ".promisor"};
+       static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor"};
        int i;
        struct strbuf buf = STRBUF_INIT;
        size_t plen;
@@ -853,6 +863,7 @@ static void prepare_pack(const char *full_name, size_t full_name_len,
        if (!strcmp(file_name, "multi-pack-index"))
                return;
        if (ends_with(file_name, ".idx") ||
+           ends_with(file_name, ".rev") ||
            ends_with(file_name, ".pack") ||
            ends_with(file_name, ".bitmap") ||
            ends_with(file_name, ".keep") ||
index a58fc738e06319624b666c80e2636217400cdda9..4cfec9e8d3cc3e2b82f7a9e3cae636db8fc5a11c 100644 (file)
@@ -90,6 +90,7 @@ uint32_t get_pack_fanout(struct packed_git *p, uint32_t value);
 
 unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
 void close_pack_windows(struct packed_git *);
+void close_pack_revindex(struct packed_git *);
 void close_pack(struct packed_git *);
 void close_object_store(struct raw_object_store *o);
 void unuse_pack(struct pack_window **);
diff --git a/pager.c b/pager.c
index ee435de67562dc5a3142a153e41672c292924640..3d37dd7adaa27ed1b6bc3952711b11ff6dfc0f56 100644 (file)
--- a/pager.c
+++ b/pager.c
 static struct child_process pager_process = CHILD_PROCESS_INIT;
 static const char *pager_program;
 
-static void wait_for_pager(int in_signal)
+static void close_pager_fds(void)
 {
-       if (!in_signal) {
-               fflush(stdout);
-               fflush(stderr);
-       }
        /* signal EOF to pager */
        close(1);
        close(2);
-       if (in_signal)
-               finish_command_in_signal(&pager_process);
-       else
-               finish_command(&pager_process);
 }
 
 static void wait_for_pager_atexit(void)
 {
-       wait_for_pager(0);
+       fflush(stdout);
+       fflush(stderr);
+       close_pager_fds();
+       finish_command(&pager_process);
 }
 
 static void wait_for_pager_signal(int signo)
 {
-       wait_for_pager(1);
+       close_pager_fds();
+       finish_command_in_signal(&pager_process);
        sigchain_pop(signo);
        raise(signo);
 }
index f0507432ee35ba8646a510d1a28ec3561f6bcb30..fbea16eaf5c2051f062bcf8b34ab99c3a4313075 100644 (file)
@@ -869,7 +869,7 @@ int parse_options(int argc, const char **argv, const char *prefix,
                usage_with_options(usagestr, options);
        }
 
-       precompose_argv(argc, argv);
+       precompose_argv_prefix(argc, argv, NULL);
        free(real_options);
        free(ctx.alias_groups);
        return parse_options_end(&ctx);
index b9950f10c8c486b16a2b916b0898d88c8d82fb59..a3cc7c94a3d5c881a0eb9037a06c1a7cf8803bbd 100644 (file)
@@ -11,6 +11,7 @@
 #include "pretty.h"
 #include "userdiff.h"
 #include "apply.h"
+#include "revision.h"
 
 struct patch_util {
        /* For the search for an exact match */
@@ -80,6 +81,8 @@ static int read_patches(const char *range, struct string_list *list,
                finish_command(&cp);
                return -1;
        }
+       if (finish_command(&cp))
+               return -1;
 
        line = contents.buf;
        size = contents.len;
@@ -97,10 +100,10 @@ static int read_patches(const char *range, struct string_list *list,
                        if (get_oid(p, &util->oid)) {
                                error(_("could not parse commit '%s'"), p);
                                free(util);
+                               free(current_filename);
                                string_list_clear(list, 1);
                                strbuf_release(&buf);
                                strbuf_release(&contents);
-                               finish_command(&cp);
                                return -1;
                        }
                        util->matching = -1;
@@ -112,10 +115,10 @@ static int read_patches(const char *range, struct string_list *list,
                        error(_("could not parse first line of `log` output: "
                                "did not start with 'commit ': '%s'"),
                              line);
+                       free(current_filename);
                        string_list_clear(list, 1);
                        strbuf_release(&buf);
                        strbuf_release(&contents);
-                       finish_command(&cp);
                        return -1;
                }
 
@@ -133,9 +136,16 @@ static int read_patches(const char *range, struct string_list *list,
                        orig_len = len;
                        len = parse_git_diff_header(&root, &linenr, 0, line,
                                                    len, size, &patch);
-                       if (len < 0)
-                               die(_("could not parse git header '%.*s'"),
-                                   orig_len, line);
+                       if (len < 0) {
+                               error(_("could not parse git header '%.*s'"),
+                                     orig_len, line);
+                               free(util);
+                               free(current_filename);
+                               string_list_clear(list, 1);
+                               strbuf_release(&buf);
+                               strbuf_release(&contents);
+                               return -1;
+                       }
                        strbuf_addstr(&buf, " ## ");
                        if (patch.is_new > 0)
                                strbuf_addf(&buf, "%s (new)", patch.new_name);
@@ -218,9 +228,6 @@ static int read_patches(const char *range, struct string_list *list,
        strbuf_release(&buf);
        free(current_filename);
 
-       if (finish_command(&cp))
-               return -1;
-
        return 0;
 }
 
@@ -458,12 +465,35 @@ static void patch_diff(const char *a, const char *b,
        diff_flush(diffopt);
 }
 
+static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
+{
+       return data;
+}
+
 static void output(struct string_list *a, struct string_list *b,
-                  struct diff_options *diffopt)
+                  struct range_diff_options *range_diff_opts)
 {
        struct strbuf buf = STRBUF_INIT, dashes = STRBUF_INIT;
        int patch_no_width = decimal_width(1 + (a->nr > b->nr ? a->nr : b->nr));
        int i = 0, j = 0;
+       struct diff_options opts;
+       struct strbuf indent = STRBUF_INIT;
+
+       if (range_diff_opts->diffopt)
+               memcpy(&opts, range_diff_opts->diffopt, sizeof(opts));
+       else
+               diff_setup(&opts);
+
+       if (!opts.output_format)
+               opts.output_format = DIFF_FORMAT_PATCH;
+       opts.flags.suppress_diff_headers = 1;
+       opts.flags.dual_color_diffed_diffs =
+               range_diff_opts->dual_color;
+       opts.flags.suppress_hunk_header_line_count = 1;
+       opts.output_prefix = output_prefix_cb;
+       strbuf_addstr(&indent, "    ");
+       opts.output_prefix_data = &indent;
+       diff_setup_done(&opts);
 
        /*
         * We assume the user is really more interested in the second argument
@@ -484,7 +514,8 @@ static void output(struct string_list *a, struct string_list *b,
 
                /* Show unmatched LHS commit whose predecessors were shown. */
                if (i < a->nr && a_util->matching < 0) {
-                       output_pair_header(diffopt, patch_no_width,
+                       if (!range_diff_opts->right_only)
+                               output_pair_header(&opts, patch_no_width,
                                           &buf, &dashes, a_util, NULL);
                        i++;
                        continue;
@@ -492,7 +523,8 @@ static void output(struct string_list *a, struct string_list *b,
 
                /* Show unmatched RHS commits. */
                while (j < b->nr && b_util->matching < 0) {
-                       output_pair_header(diffopt, patch_no_width,
+                       if (!range_diff_opts->left_only)
+                               output_pair_header(&opts, patch_no_width,
                                           &buf, &dashes, NULL, b_util);
                        b_util = ++j < b->nr ? b->items[j].util : NULL;
                }
@@ -500,63 +532,41 @@ static void output(struct string_list *a, struct string_list *b,
                /* Show matching LHS/RHS pair. */
                if (j < b->nr) {
                        a_util = a->items[b_util->matching].util;
-                       output_pair_header(diffopt, patch_no_width,
+                       output_pair_header(&opts, patch_no_width,
                                           &buf, &dashes, a_util, b_util);
-                       if (!(diffopt->output_format & DIFF_FORMAT_NO_OUTPUT))
+                       if (!(opts.output_format & DIFF_FORMAT_NO_OUTPUT))
                                patch_diff(a->items[b_util->matching].string,
-                                          b->items[j].string, diffopt);
+                                          b->items[j].string, &opts);
                        a_util->shown = 1;
                        j++;
                }
        }
        strbuf_release(&buf);
        strbuf_release(&dashes);
-}
-
-static struct strbuf *output_prefix_cb(struct diff_options *opt, void *data)
-{
-       return data;
+       strbuf_release(&indent);
 }
 
 int show_range_diff(const char *range1, const char *range2,
-                   int creation_factor, int dual_color,
-                   const struct diff_options *diffopt,
-                   const struct strvec *other_arg)
+                   struct range_diff_options *range_diff_opts)
 {
        int res = 0;
 
        struct string_list branch1 = STRING_LIST_INIT_DUP;
        struct string_list branch2 = STRING_LIST_INIT_DUP;
 
-       if (read_patches(range1, &branch1, other_arg))
+       if (range_diff_opts->left_only && range_diff_opts->right_only)
+               res = error(_("--left-only and --right-only are mutually exclusive"));
+
+       if (!res && read_patches(range1, &branch1, range_diff_opts->other_arg))
                res = error(_("could not parse log for '%s'"), range1);
-       if (!res && read_patches(range2, &branch2, other_arg))
+       if (!res && read_patches(range2, &branch2, range_diff_opts->other_arg))
                res = error(_("could not parse log for '%s'"), range2);
 
        if (!res) {
-               struct diff_options opts;
-               struct strbuf indent = STRBUF_INIT;
-
-               if (diffopt)
-                       memcpy(&opts, diffopt, sizeof(opts));
-               else
-                       diff_setup(&opts);
-
-               if (!opts.output_format)
-                       opts.output_format = DIFF_FORMAT_PATCH;
-               opts.flags.suppress_diff_headers = 1;
-               opts.flags.dual_color_diffed_diffs = dual_color;
-               opts.flags.suppress_hunk_header_line_count = 1;
-               opts.output_prefix = output_prefix_cb;
-               strbuf_addstr(&indent, "    ");
-               opts.output_prefix_data = &indent;
-               diff_setup_done(&opts);
-
                find_exact_matches(&branch1, &branch2);
-               get_correspondences(&branch1, &branch2, creation_factor);
-               output(&branch1, &branch2, &opts);
-
-               strbuf_release(&indent);
+               get_correspondences(&branch1, &branch2,
+                                   range_diff_opts->creation_factor);
+               output(&branch1, &branch2, range_diff_opts);
        }
 
        string_list_clear(&branch1, 1);
@@ -564,3 +574,31 @@ int show_range_diff(const char *range1, const char *range2,
 
        return res;
 }
+
+int is_range_diff_range(const char *arg)
+{
+       char *copy = xstrdup(arg); /* setup_revisions() modifies it */
+       const char *argv[] = { "", copy, "--", NULL };
+       int i, positive = 0, negative = 0;
+       struct rev_info revs;
+
+       init_revisions(&revs, NULL);
+       if (setup_revisions(3, argv, &revs, NULL) == 1) {
+               for (i = 0; i < revs.pending.nr; i++)
+                       if (revs.pending.objects[i].item->flags & UNINTERESTING)
+                               negative++;
+                       else
+                               positive++;
+               for (i = 0; i < revs.pending.nr; i++) {
+                       struct object *obj = revs.pending.objects[i].item;
+
+                       if (obj->type == OBJ_COMMIT)
+                               clear_commit_marks((struct commit *)obj,
+                                                  ALL_REV_FLAGS);
+               }
+       }
+
+       free(copy);
+       object_array_clear(&revs.pending);
+       return negative > 0 && positive > 0;
+}
index 583ced2e8e749003b9a22fe2496d0db49f5ba7f9..04ffe217be67c32d378a94f90094b70b01e55548 100644 (file)
@@ -6,14 +6,25 @@
 
 #define RANGE_DIFF_CREATION_FACTOR_DEFAULT 60
 
+struct range_diff_options {
+       int creation_factor;
+       unsigned dual_color:1;
+       unsigned left_only:1, right_only:1;
+       const struct diff_options *diffopt; /* may be NULL */
+       const struct strvec *other_arg; /* may be NULL */
+};
+
 /*
- * Compare series of commits in RANGE1 and RANGE2, and emit to the
- * standard output.  NULL can be passed to DIFFOPT to use the built-in
- * default.
+ * Compare series of commits in `range1` and `range2`, and emit to the
+ * standard output.
  */
 int show_range_diff(const char *range1, const char *range2,
-                   int creation_factor, int dual_color,
-                   const struct diff_options *diffopt,
-                   const struct strvec *other_arg);
+                   struct range_diff_options *opts);
+
+/*
+ * Determine whether the given argument is usable as a range argument of `git
+ * range-diff`, e.g. A..B.
+ */
+int is_range_diff_range(const char *arg);
 
 #endif
index fd994e18744cebd22e68fd1d969fb0f621d2362e..bade6528ee36e1efd0a28aeedcb5202f7cc7910d 100644 (file)
@@ -1210,12 +1210,20 @@ static void grab_person(const char *who, struct atom_value *val, int deref, void
 }
 
 static void find_subpos(const char *buf,
-                       const char **sub, unsigned long *sublen,
-                       const char **body, unsigned long *bodylen,
-                       unsigned long *nonsiglen,
-                       const char **sig, unsigned long *siglen)
+                       const char **sub, size_t *sublen,
+                       const char **body, size_t *bodylen,
+                       size_t *nonsiglen,
+                       const char **sig, size_t *siglen)
 {
+       struct strbuf payload = STRBUF_INIT;
+       struct strbuf signature = STRBUF_INIT;
        const char *eol;
+       const char *end = buf + strlen(buf);
+       const char *sigstart;
+
+       /* parse signature first; we might not even have a subject line */
+       parse_signature(buf, end - buf, &payload, &signature);
+
        /* skip past header until we hit empty line */
        while (*buf && *buf != '\n') {
                eol = strchrnul(buf, '\n');
@@ -1226,16 +1234,14 @@ static void find_subpos(const char *buf,
        /* skip any empty lines */
        while (*buf == '\n')
                buf++;
-
-       /* parse signature first; we might not even have a subject line */
-       *sig = buf + parse_signature(buf, strlen(buf));
-       *siglen = strlen(*sig);
+       *sig = strbuf_detach(&signature, siglen);
+       sigstart = buf + parse_signed_buffer(buf, strlen(buf));
 
        /* subject is first non-empty line */
        *sub = buf;
        /* subject goes to first empty line before signature begins */
        if ((eol = strstr(*sub, "\n\n"))) {
-               eol = eol < *sig ? eol : *sig;
+               eol = eol < sigstart ? eol : sigstart;
        /* check if message uses CRLF */
        } else if (! (eol = strstr(*sub, "\r\n\r\n"))) {
                /* treat whole message as subject */
@@ -1253,7 +1259,7 @@ static void find_subpos(const char *buf,
                buf++;
        *body = buf;
        *bodylen = strlen(buf);
-       *nonsiglen = *sig - buf;
+       *nonsiglen = sigstart - buf;
 }
 
 /*
@@ -1285,12 +1291,13 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
 {
        int i;
        const char *subpos = NULL, *bodypos = NULL, *sigpos = NULL;
-       unsigned long sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
+       size_t sublen = 0, bodylen = 0, nonsiglen = 0, siglen = 0;
 
        for (i = 0; i < used_atom_cnt; i++) {
                struct used_atom *atom = &used_atom[i];
                const char *name = atom->name;
                struct atom_value *v = &val[i];
+
                if (!!deref != (*name == '*'))
                        continue;
                if (deref)
@@ -1322,7 +1329,7 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
                        v->s = xmemdupz(sigpos, siglen);
                else if (atom->u.contents.option == C_LINES) {
                        struct strbuf s = STRBUF_INIT;
-                       const char *contents_end = bodylen + bodypos - siglen;
+                       const char *contents_end = bodypos + nonsiglen;
 
                        /*  Size is the length of the message after removing the signature */
                        append_lines(&s, subpos, contents_end - subpos, atom->u.contents.nlines);
@@ -1336,7 +1343,9 @@ static void grab_sub_body_contents(struct atom_value *val, int deref, void *buf)
                        v->s = strbuf_detach(&s, NULL);
                } else if (atom->u.contents.option == C_BARE)
                        v->s = xstrdup(subpos);
+
        }
+       free((void *)sigpos);
 }
 
 /*
index aad1a0f0808b44ddbfa8b17562cc8636ac2ac28e..5a5919825285745ff11f5a8f7248ebaaddd0560a 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -6,6 +6,8 @@
 #include "hashmap.h"
 #include "refspec.h"
 
+struct transport_ls_refs_options;
+
 /**
  * The API gives access to the configuration related to remotes. It handles
  * all three configuration mechanisms historically and currently used by Git,
@@ -196,7 +198,7 @@ struct ref **get_remote_heads(struct packet_reader *reader,
 /* Used for protocol v2 in order to retrieve refs from a remote */
 struct ref **get_remote_refs(int fd_out, struct packet_reader *reader,
                             struct ref **list, int for_push,
-                            const struct strvec *ref_prefixes,
+                            struct transport_ls_refs_options *transport_options,
                             const struct string_list *server_options,
                             int stateless_rpc);
 
index 3efd994160d95f69f2e9df71a04c72bac6a062e3..b78733f5089b0dcef8eed71c44243a3b9a6f86db 100644 (file)
@@ -3270,7 +3270,7 @@ define_commit_slab(indegree_slab, int);
 define_commit_slab(author_date_slab, timestamp_t);
 
 struct topo_walk_info {
-       uint32_t min_generation;
+       timestamp_t min_generation;
        struct prio_queue explore_queue;
        struct prio_queue indegree_queue;
        struct prio_queue topo_queue;
@@ -3338,7 +3338,7 @@ static void explore_walk_step(struct rev_info *revs)
 }
 
 static void explore_to_depth(struct rev_info *revs,
-                            uint32_t gen_cutoff)
+                            timestamp_t gen_cutoff)
 {
        struct topo_walk_info *info = revs->topo_walk_info;
        struct commit *c;
@@ -3367,6 +3367,9 @@ static void indegree_walk_step(struct rev_info *revs)
                struct commit *parent = p->item;
                int *pi = indegree_slab_at(&info->indegree, parent);
 
+               if (repo_parse_commit_gently(revs->repo, parent, 1) < 0)
+                       return;
+
                if (*pi)
                        (*pi)++;
                else
@@ -3380,7 +3383,7 @@ static void indegree_walk_step(struct rev_info *revs)
 }
 
 static void compute_indegrees_to_depth(struct rev_info *revs,
-                                      uint32_t gen_cutoff)
+                                      timestamp_t gen_cutoff)
 {
        struct topo_walk_info *info = revs->topo_walk_info;
        struct commit *c;
@@ -3438,7 +3441,7 @@ static void init_topo_walk(struct rev_info *revs)
        info->min_generation = GENERATION_NUMBER_INFINITY;
        for (list = revs->commits; list; list = list->next) {
                struct commit *c = list->item;
-               uint32_t generation;
+               timestamp_t generation;
 
                if (repo_parse_commit_gently(revs->repo, c, 1))
                        continue;
@@ -3506,7 +3509,7 @@ static void expand_topo_walk(struct rev_info *revs, struct commit *commit)
        for (p = commit->parents; p; p = p->next) {
                struct commit *parent = p->item;
                int *pi;
-               uint32_t generation;
+               timestamp_t generation;
 
                if (parent->object.flags & UNINTERESTING)
                        continue;
index ea4d0fb4b154c89a0fcd12d29e41ffcbb2af0302..509841bf273d821df41c0f0aef86d5768dda9e0b 100644 (file)
@@ -551,8 +551,11 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
 
        while ((waiting = waitpid(pid, &status, 0)) < 0 && errno == EINTR)
                ;       /* nothing */
-       if (in_signal)
-               return 0;
+       if (in_signal) {
+               if (WIFEXITED(status))
+                       code = WEXITSTATUS(status);
+               return code;
+       }
 
        if (waiting < 0) {
                failed_errno = errno;
diff --git a/serve.c b/serve.c
index eec2fe6f294cd1c0c5e8a76b66b09d78dd4e22da..ac20c7276304c51a2169e3d95777a05a1d6c1e31 100644 (file)
--- a/serve.c
+++ b/serve.c
@@ -73,7 +73,7 @@ struct protocol_capability {
 
 static struct protocol_capability capabilities[] = {
        { "agent", agent_advertise, NULL },
-       { "ls-refs", always_advertise, ls_refs },
+       { "ls-refs", ls_refs_advertise, ls_refs },
        { "fetch", upload_pack_advertise, upload_pack_v2 },
        { "server-option", always_advertise, NULL },
        { "object-format", object_format_advertise, NULL },
index df05434d32cc1fa720d5408c26c22371d1f0e9e8..dafa17c3e616756d38dd9c0dba45de978c8088ff 100644 (file)
@@ -1,13 +1,13 @@
 t[0-9][0-9][0-9][0-9]/* -whitespace
 /chainlint/*.expect eol=lf
-/diff-lib/* eol=lf
+/lib-diff/* eol=lf
 /t0110/url-* binary
 /t3206/* eol=lf
 /t3900/*.txt eol=lf
 /t3901/*.txt eol=lf
-/t4034/*/* eol=lf
 /t4013/* eol=lf
 /t4018/* eol=lf
+/t4034/*/* eol=lf
 /t4051/* eol=lf
 /t4100/* eol=lf
 /t4101/* eol=lf
index 1d80fb7004145d9d5666efeda08923ec82533154..593d4a4e270cc07b0f8e7d93e0da4d3c0b73eebd 100644 (file)
--- a/t/README
+++ b/t/README
@@ -387,6 +387,9 @@ GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to
 be written after every 'git commit' command, and overrides the
 'core.commitGraph' setting to true.
 
+GIT_TEST_COMMIT_GRAPH_NO_GDAT=<boolean>, when true, forces the
+commit-graph to be written without generation data chunk.
+
 GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=<boolean>, when true, forces
 commit-graph write to compute and write changed path Bloom filters for
 every 'git commit-graph write', as if the `--changed-paths` option was
@@ -433,6 +436,9 @@ GIT_TEST_DEFAULT_HASH=<hash-algo> specifies which hash algorithm to
 use in the test scripts. Recognized values for <hash-algo> are "sha1"
 and "sha256".
 
+GIT_TEST_WRITE_REV_INDEX=<boolean>, when true enables the
+'pack.writeReverseIndex' setting.
+
 Naming Tests
 ------------
 
@@ -911,13 +917,13 @@ library for your script to use.
 
    Check whether a file has the length it is expected to.
 
- - test_path_is_file <path> [<diagnosis>]
-   test_path_is_dir <path> [<diagnosis>]
-   test_path_is_missing <path> [<diagnosis>]
+ - test_path_is_file <path>
+   test_path_is_dir <path>
+   test_path_is_missing <path>
 
    Check if the named path is a file, if the named path is a
    directory, or if the named path does not exist, respectively,
-   and fail otherwise, showing the <diagnosis> text.
+   and fail otherwise.
 
  - test_when_finished <script>
 
index 5f585a17256de1f4ee938e29a7e74db26f244e90..75927b2c81d28de1b3f16de0ef4a9d55aad5e668 100644 (file)
@@ -33,6 +33,10 @@ int cmd__read_graph(int argc, const char **argv)
                printf(" oid_lookup");
        if (graph->chunk_commit_data)
                printf(" commit_metadata");
+       if (graph->chunk_generation_data)
+               printf(" generation_data");
+       if (graph->chunk_generation_data_overflow)
+               printf(" generation_data_overflow");
        if (graph->chunk_extra_edges)
                printf(" extra_edges");
        if (graph->chunk_bloom_indexes)
index 823f33ceff4aadac07128786f01c8a38a223cdcf..f93633f895a1926aa6ea4ca712305cccb5826a2d 100644 (file)
@@ -198,6 +198,14 @@ static int ut_006data(int argc, const char **argv)
        return 0;
 }
 
+static int ut_007bug(int argc, const char **argv)
+{
+       /*
+        * Exercise BUG() to ensure that the message is printed to trace2.
+        */
+       BUG("the bug message");
+}
+
 /*
  * Usage:
  *     test-tool trace2 <ut_name_1> <ut_usage_1>
@@ -214,6 +222,7 @@ static struct unit_test ut_table[] = {
        { ut_004child,    "004child",  "[<child_command_line>]" },
        { ut_005exec,     "005exec",   "<git_command_args>" },
        { ut_006data,     "006data",   "[<category> <key> <value>]+" },
+       { ut_007bug,      "007bug",    "" },
 };
 /* clang-format on */
 
diff --git a/t/lib-bitmap.sh b/t/lib-bitmap.sh
new file mode 100644 (file)
index 0000000..fe3f98b
--- /dev/null
@@ -0,0 +1,26 @@
+# Compare a file containing rev-list bitmap traversal output to its non-bitmap
+# counterpart. You can't just use test_cmp for this, because the two produce
+# subtly different output:
+#
+#   - regular output is in traversal order, whereas bitmap is split by type,
+#     with non-packed objects at the end
+#
+#   - regular output has a space and the pathname appended to non-commit
+#     objects; bitmap output omits this
+#
+# This function normalizes and compares the two. The second file should
+# always be the bitmap output.
+test_bitmap_traversal () {
+       if test "$1" = "--no-confirm-bitmaps"
+       then
+               shift
+       elif cmp "$1" "$2"
+       then
+               echo >&2 "identical raw outputs; are you sure bitmaps were used?"
+               return 1
+       fi &&
+       cut -d' ' -f1 "$1" | sort >"$1.normalized" &&
+       sort "$2" >"$2.normalized" &&
+       test_cmp "$1.normalized" "$2.normalized" &&
+       rm -f "$1.normalized" "$2.normalized"
+}
similarity index 100%
rename from t/test-bundle-functions.sh
rename to t/lib-bundle.sh
index dea2cbef5172954666f9ee40142273252b6fe7a2..5ea8bc9f1dcfe644da4b9ce720f2a523ff172475 100644 (file)
@@ -20,7 +20,7 @@ check() {
                false
        fi &&
        test_cmp expect-stdout stdout &&
-       test_i18ncmp expect-stderr stderr
+       test_cmp expect-stderr stderr
 }
 
 read_chunk() {
similarity index 100%
rename from t/diff-lib.sh
rename to t/lib-diff.sh
similarity index 100%
rename from t/diff-lib/COPYING
rename to t/lib-diff/COPYING
similarity index 100%
rename from t/diff-lib/README
rename to t/lib-diff/README
similarity index 100%
rename from t/gitweb-lib.sh
rename to t/lib-gitweb.sh
index 1184cceef262283ac65c99985cf9f4ebb3edce68..bf952ef9204dbbff379803810d4ec8ef87749a35 100644 (file)
@@ -12,13 +12,13 @@ sanitize_log_output () {
 lib_test_cmp_graph () {
        git log --graph "$@" >output &&
        sed 's/ *$//' >output.sanitized <output &&
-       test_i18ncmp expect output.sanitized
+       test_cmp expect output.sanitized
 }
 
 lib_test_cmp_short_graph () {
        git log --graph --pretty=short "$@" >output &&
        sanitize_log_output >output.sanitized <output &&
-       test_i18ncmp expect output.sanitized
+       test_cmp expect output.sanitized
 }
 
 lib_test_cmp_colored_graph () {
index a6e570d674ac97c33396a605cbc752f9f121bb2e..705d62cc27a574a990978f786cf45c864ebffd50 100755 (executable)
@@ -271,7 +271,7 @@ test_expect_success 'pretend we have a mix of all possible results' '
        EOF
 '
 
-test_expect_success C_LOCALE_OUTPUT 'test --verbose' '
+test_expect_success 'test --verbose' '
        run_sub_test_lib_test_err \
                t1234-verbose "test verbose" --verbose <<-\EOF &&
        test_expect_success "passing test" true
index 75ee9a96b80bbad5506bc6961b23988358a65c8a..6b757d7169252b467ed18c4f209c0428e286bba3 100755 (executable)
@@ -11,7 +11,7 @@ check_relative() {
        echo "$t -> $2" >expect
        test_expect_${3:-success} "relative date ($2)" "
        test-tool date relative $t >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
        "
 }
 
@@ -139,7 +139,7 @@ check_date_format_human() {
        echo "$t -> $2" >expect
        test_expect_success "human date $t" '
                test-tool date human $t >actual &&
-               test_i18ncmp expect actual
+               test_cmp expect actual
 '
 }
 
index 370a389e5c5c509b51189e23b81c6ce2d1b79a18..f7abde62f633ee2ff89614777fd03533559cb887 100755 (executable)
@@ -34,7 +34,7 @@ expect_from_stdin () {
 test_stderr () {
        expected="$1"
        expect_in stderr "$1" &&
-       test_i18ncmp "$HOME/expected-stderr" "$HOME/stderr"
+       test_cmp "$HOME/expected-stderr" "$HOME/stderr"
 }
 
 broken_c_unquote () {
index e8ef7300ecb8693367906d4c314797e5750bdf50..5679e29c62479c663446e76fbc587a2648efe601 100755 (executable)
@@ -55,7 +55,7 @@ test_expect_success "--help does not work for guides" "
                git: 'revisions' is not a git command. See 'git --help'.
        EOF
        test_must_fail git revisions --help 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 "
 
 test_expect_success 'git help' '
index e03554d2f3457b354aa4035d446ffb856061e9a3..39e5e4b34f8729e361f7cc7801dadb4db6d6c76b 100755 (executable)
@@ -10,7 +10,7 @@ test_expect_success 'advice should be printed when config variable is unset' '
        hint: Disable this message with "git config advice.nestedTag false"
        EOF
        test-tool advise "This is a piece of advice" 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'advice should be printed when config variable is set to true' '
@@ -20,7 +20,7 @@ test_expect_success 'advice should be printed when config variable is set to tru
        EOF
        test_config advice.nestedTag true &&
        test-tool advise "This is a piece of advice" 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'advice should not be printed when config variable is set to false' '
index 375cf9439858d974b9db8f826062a9577d70af9d..f25ae8b5e1f34dd8e58bbe65ee171cbf5c8aa14b 100755 (executable)
@@ -87,10 +87,8 @@ test_expect_success 'safecrlf: print warning only once' '
        git commit -m "nowarn" &&
        for w in Oh here is CRLFQ in text; do echo $w; done | q_to_cr >doublewarn &&
        git add doublewarn 2>err &&
-       if test_have_prereq C_LOCALE_OUTPUT
-       then
-               test $(grep "CRLF will be replaced by LF" err | wc -l) = 1
-       fi
+       grep "CRLF will be replaced by LF" err >err.warnings &&
+       test_line_count = 1 err.warnings
 '
 
 
index e4c4de5c74563a6ded58c0f553d07a7e59cd2b78..e828ee964c1b86f307d7522ce280ee2483348111 100755 (executable)
@@ -34,7 +34,7 @@ filter_git () {
 # Compare two files and ensure that `clean` and `smudge` respectively are
 # called at least once if specified in the `expect` file. The actual
 # invocation count is not relevant because their number can vary.
-# c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+# c.f. https://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
 test_cmp_count () {
        expect=$1
        actual=$2
@@ -49,7 +49,7 @@ test_cmp_count () {
 
 # Compare two files but exclude all `clean` invocations because Git can
 # call `clean` zero or more times.
-# c.f. http://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
+# c.f. https://lore.kernel.org/git/xmqqshv18i8i.fsf@gitster.mtv.corp.google.com/
 test_cmp_exclude_clean () {
        expect=$1
        actual=$2
index 51f74a3ddf3fc9ee617134a0e947dfc60e23aba2..d24d5acfbc7947c97250da98d50b8612ef0da459 100755 (executable)
@@ -83,7 +83,7 @@ check_warning () {
        *) echo >&2 "Illegal 1": "$1" ; return false ;;
        esac
        grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" | uniq  >"$2".actual
-       test_i18ncmp "$2".expect "$2".actual
+       test_cmp "$2".expect "$2".actual
 }
 
 commit_check_warn () {
index 14cafc138b7875013afeee01d74dbbeb2e56d495..ad4746d899a48b6eef46f7690d291c0ec6411d5b 100755 (executable)
@@ -61,7 +61,7 @@ EOF
 test_expect_success 'test help' '
        test_must_fail test-tool parse-options -h >output 2>output.err &&
        test_must_be_empty output.err &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 mv expect expect.err
@@ -84,7 +84,7 @@ check_unknown_i18n() {
        cat expect.err >>expect &&
        test_must_fail test-tool parse-options $* >output 2>output.err &&
        test_must_be_empty output &&
-       test_i18ncmp expect output.err
+       test_cmp expect output.err
 }
 
 test_expect_success 'OPT_BOOL() #1' 'check boolean: 1 --yes'
@@ -250,7 +250,7 @@ EOF
 test_expect_success 'detect possible typos' '
        test_must_fail test-tool parse-options -boolean >output 2>output.err &&
        test_must_be_empty output &&
-       test_i18ncmp typo.err output.err
+       test_cmp typo.err output.err
 '
 
 cat >typo.err <<\EOF
@@ -260,7 +260,7 @@ EOF
 test_expect_success 'detect possible typos' '
        test_must_fail test-tool parse-options -ambiguous >output 2>output.err &&
        test_must_be_empty output &&
-       test_i18ncmp typo.err output.err
+       test_cmp typo.err output.err
 '
 
 test_expect_success 'keep some options as arguments' '
index 5a633690bffc8ac0783f668abb5a96af557515b3..9bf66c9e68dd7b99434dfeaa19a0cc8a7618f039 100755 (executable)
@@ -10,40 +10,36 @@ cache-tree extension.
 cmp_cache_tree () {
        test-tool dump-cache-tree | sed -e '/#(ref)/d' >actual &&
        sed "s/$OID_REGEX/SHA/" <actual >filtered &&
-       test_cmp "$1" filtered
+       test_cmp "$1" filtered &&
+       rm filtered
 }
 
 # We don't bother with actually checking the SHA1:
 # test-tool dump-cache-tree already verifies that all existing data is
 # correct.
-generate_expected_cache_tree_rec () {
-       dir="$1${1:+/}" &&
-       parent="$2" &&
-       # ls-files might have foo/bar, foo/bar/baz, and foo/bar/quux
-       # We want to count only foo because it's the only direct child
-       git ls-files >files &&
-       subtrees=$(grep / files|cut -d / -f 1|uniq) &&
-       subtree_count=$(echo "$subtrees"|awk -v c=0 '$1 != "" {++c} END {print c}') &&
-       entries=$(wc -l <files) &&
-       printf "SHA $dir (%d entries, %d subtrees)\n" "$entries" "$subtree_count" &&
-       for subtree in $subtrees
-       do
-               cd "$subtree"
-               generate_expected_cache_tree_rec "$dir$subtree" "$dir" || return 1
-               cd ..
-       done &&
-       dir=$parent
-}
-
 generate_expected_cache_tree () {
-       (
-               generate_expected_cache_tree_rec
-       )
+       pathspec="$1" &&
+       dir="$2${2:+/}" &&
+       git ls-tree --name-only HEAD -- "$pathspec" >files &&
+       git ls-tree --name-only -d HEAD -- "$pathspec" >subtrees &&
+       printf "SHA %s (%d entries, %d subtrees)\n" "$dir" $(wc -l <files) $(wc -l <subtrees) &&
+       while read subtree
+       do
+               generate_expected_cache_tree "$pathspec/$subtree/" "$subtree" || return 1
+       done <subtrees
 }
 
 test_cache_tree () {
-       generate_expected_cache_tree >expect &&
-       cmp_cache_tree expect
+       generate_expected_cache_tree "." >expect &&
+       cmp_cache_tree expect &&
+       rm expect actual files subtrees &&
+       git status --porcelain -- ':!status' ':!expected.status' >status &&
+       if test -n "$1"
+       then
+               test_cmp "$1" status
+       else
+               test_must_be_empty status
+       fi
 }
 
 test_invalid_cache_tree () {
@@ -54,7 +50,7 @@ test_invalid_cache_tree () {
 }
 
 test_no_cache_tree () {
-       >expect &&
+       >expect &&
        cmp_cache_tree expect
 }
 
@@ -83,18 +79,6 @@ test_expect_success 'git-add in subdir invalidates cache-tree' '
        test_invalid_cache_tree
 '
 
-cat >before <<\EOF
-SHA  (3 entries, 2 subtrees)
-SHA dir1/ (1 entries, 0 subtrees)
-SHA dir2/ (1 entries, 0 subtrees)
-EOF
-
-cat >expect <<\EOF
-invalid                                   (2 subtrees)
-invalid                                  dir1/ (0 subtrees)
-SHA dir2/ (1 entries, 0 subtrees)
-EOF
-
 test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
        git tag no-children &&
        test_when_finished "git reset --hard no-children; git read-tree HEAD" &&
@@ -102,9 +86,20 @@ test_expect_success 'git-add in subdir does not invalidate sibling cache-tree' '
        test_commit dir1/a &&
        test_commit dir2/b &&
        echo "I changed this file" >dir1/a &&
+       test_when_finished "rm before" &&
+       cat >before <<-\EOF &&
+       SHA  (3 entries, 2 subtrees)
+       SHA dir1/ (1 entries, 0 subtrees)
+       SHA dir2/ (1 entries, 0 subtrees)
+       EOF
        cmp_cache_tree before &&
        echo "I changed this file" >dir1/a &&
        git add dir1/a &&
+       cat >expect <<-\EOF &&
+       invalid                                   (2 subtrees)
+       invalid                                  dir1/ (0 subtrees)
+       SHA dir2/ (1 entries, 0 subtrees)
+       EOF
        cmp_cache_tree expect
 '
 
@@ -133,6 +128,7 @@ test_expect_success 'second commit has cache-tree' '
 '
 
 test_expect_success PERL 'commit --interactive gives cache-tree on partial commit' '
+       test_when_finished "git reset --hard" &&
        cat <<-\EOT >foo.c &&
        int foo()
        {
@@ -159,7 +155,10 @@ test_expect_success PERL 'commit --interactive gives cache-tree on partial commi
        EOT
        test_write_lines p 1 "" s n y q |
        git commit --interactive -m foo &&
-       test_cache_tree
+       cat <<-\EOF >expected.status &&
+        M foo.c
+       EOF
+       test_cache_tree expected.status
 '
 
 test_expect_success PERL 'commit -p with shrinking cache-tree' '
@@ -250,7 +249,10 @@ test_expect_success 'partial commit gives cache-tree' '
        git add one.t &&
        echo "some other change" >two.t &&
        git commit two.t -m partial &&
-       test_cache_tree
+       cat <<-\EOF >expected.status &&
+       M  one.t
+       EOF
+       test_cache_tree expected.status
 '
 
 test_expect_success 'no phantom error when switching trees' '
index 90da1c7ddc41b73dc07b4433255d3b0ffd66bd5d..6c74df0dc67e575d171c8ad4a4ab57465b1e622c 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_TEST_FALLBACKS is set' '
     test -n "$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS"
 '
 
-test_expect_success C_LOCALE_OUTPUT 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is fallthrough' '
+test_expect_success 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is fallthrough' '
     echo fallthrough >expect &&
     echo $GIT_INTERNAL_GETTEXT_SH_SCHEME >actual &&
     test_cmp expect actual
@@ -27,25 +27,25 @@ test_expect_success C_LOCALE_OUTPUT 'sanity: $GIT_INTERNAL_GETTEXT_SH_SCHEME" is
 test_expect_success 'gettext: our gettext() fallback has pass-through semantics' '
     printf "test" >expect &&
     gettext "test" >actual &&
-    test_i18ncmp expect actual &&
+    test_cmp expect actual &&
     printf "test more words" >expect &&
     gettext "test more words" >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback has pass-through semantics' '
     printf "test" >expect &&
     eval_gettext "test" >actual &&
-    test_i18ncmp expect actual &&
+    test_cmp expect actual &&
     printf "test more words" >expect &&
     eval_gettext "test more words" >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables' '
     printf "test YesPlease" >expect &&
     GIT_INTERNAL_GETTEXT_TEST_FALLBACKS=YesPlease eval_gettext "test \$GIT_INTERNAL_GETTEXT_TEST_FALLBACKS" >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces' '
@@ -53,7 +53,7 @@ test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate v
     export cmdline &&
     printf "When you have resolved this problem, run git am --resolved." >expect &&
     eval_gettext "When you have resolved this problem, run \$cmdline --resolved." >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate variables with spaces and quotes' '
@@ -61,7 +61,7 @@ test_expect_success 'eval_gettext: our eval_gettext() fallback can interpolate v
     export cmdline &&
     printf "When you have resolved this problem, run \"git am --resolved\"." >expect &&
     eval_gettext "When you have resolved this problem, run \"\$cmdline --resolved\"." >actual &&
-    test_i18ncmp expect actual
+    test_cmp expect actual
 '
 
 test_done
index ce7574edb1e772fca30044ceb8a7cf3ac2e19470..0cf3a63b75b7c1e5c121787b5d612cd49a89ef71 100755 (executable)
@@ -147,6 +147,25 @@ test_expect_success 'normal stream, error event' '
        test_cmp expect actual
 '
 
+# Verb 007bug
+#
+# Check that BUG writes to trace2
+
+test_expect_success 'BUG messages are written to trace2' '
+       test_when_finished "rm trace.normal actual expect" &&
+       test_must_fail env GIT_TRACE2="$(pwd)/trace.normal" test-tool trace2 007bug &&
+       perl "$TEST_DIRECTORY/t0210/scrub_normal.perl" <trace.normal >actual &&
+       cat >expect <<-EOF &&
+               version $V
+               start _EXE_ trace2 007bug
+               cmd_name trace2 (trace2)
+               error the bug message
+               exit elapsed:_TIME_ code:99
+               atexit elapsed:_TIME_ code:99
+       EOF
+       test_cmp expect actual
+'
+
 sane_unset GIT_TRACE2_BRIEF
 
 # Now test without environment variables and get all Trace2 settings
index a18f8a473bfc667023c433a6c55c05defcc2e9f0..3485c0534e6d39be9ef6f285fa2ec80400dc47cd 100755 (executable)
@@ -578,7 +578,7 @@ test_expect_success 'helpers can abort the process' '
        quit: host=example.com
        fatal: credential helper '\''quit'\'' told us to quit
        EOF
-       test_i18ncmp expect stderr
+       test_cmp expect stderr
 '
 
 test_expect_success 'empty helper spec resets helper list' '
@@ -606,7 +606,7 @@ test_expect_success 'url parser rejects embedded newlines' '
        warning: url contains a newline in its path component: https://one.example.com?%0ahost=two.example.com/
        fatal: credential url cannot be parsed: https://one.example.com?%0ahost=two.example.com/
        EOF
-       test_i18ncmp expect stderr
+       test_cmp expect stderr
 '
 
 test_expect_success 'host-less URLs are parsed as empty host' '
@@ -633,7 +633,7 @@ test_expect_success 'credential system refuses to work with missing host' '
        cat >expect <<-\EOF &&
        fatal: refusing to work with credential missing host field
        EOF
-       test_i18ncmp expect stderr
+       test_cmp expect stderr
 '
 
 test_expect_success 'credential system refuses to work with missing protocol' '
@@ -643,7 +643,7 @@ test_expect_success 'credential system refuses to work with missing protocol' '
        cat >expect <<-\EOF &&
        fatal: refusing to work with credential missing protocol field
        EOF
-       test_i18ncmp expect stderr
+       test_cmp expect stderr
 '
 
 # usage: check_host_and_path <url> <expected-host> <expected-path>
index 84cce345e7dd3d9d704c064ab49cc726c4c7af32..22058b503ac78c41f542eb02a5a3dbd1cb200d3e 100755 (executable)
@@ -29,7 +29,7 @@ test_expect_success 'simple progress display' '
        test-tool progress "Working hard" <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress display with total' '
@@ -48,7 +48,7 @@ test_expect_success 'progress display with total' '
        test-tool progress --total=3 "Working hard" <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #1' '
@@ -72,7 +72,7 @@ EOF
                <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #2' '
@@ -100,7 +100,7 @@ EOF
                <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #3 - even the first is too long' '
@@ -126,7 +126,7 @@ EOF
                <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress display breaks long lines #4 - title line matches terminal width' '
@@ -150,7 +150,7 @@ EOF
                <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 # Progress counter goes backwards, this should not happen in practice.
@@ -172,7 +172,7 @@ test_expect_success 'progress shortens - crazy caller' '
        test-tool progress --total=1000 "Working hard" <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress display with throughput' '
@@ -201,7 +201,7 @@ test_expect_success 'progress display with throughput' '
        test-tool progress "Working hard" <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress display with throughput and total' '
@@ -226,7 +226,7 @@ test_expect_success 'progress display with throughput and total' '
        test-tool progress --total=40 "Working hard" <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'cover up after throughput shortens' '
@@ -255,7 +255,7 @@ test_expect_success 'cover up after throughput shortens' '
        test-tool progress "Working hard" <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'cover up after throughput shortens a lot' '
@@ -280,7 +280,7 @@ test_expect_success 'cover up after throughput shortens a lot' '
        test-tool progress "Working hard" <in 2>stderr &&
 
        show_cr <stderr >out &&
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'progress generates traces' '
index dfe9794a74081dc4534966cc68ee64dd4fed9a92..24092c09a95771df742337e8d6c1c9eac77221ba 100755 (executable)
@@ -253,7 +253,7 @@ warning: The following paths were already present and thus not updated despite s
 
 After fixing the above paths, you may want to run `git sparse-checkout reapply`.
 EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'checkout without --ignore-skip-worktree-bits' '
index 936d72041babf0b07d65f47ba12fe59c7e07b48e..e0dd5d65cedadbab2a5d4aa9c0697b346374612b 100755 (executable)
@@ -675,6 +675,13 @@ test_expect_success 'invalid unit' '
        test_i18ngrep "bad numeric config value .1auto. for .aninvalid.unit. in file .git/config: invalid unit" actual
 '
 
+test_expect_success 'invalid unit boolean' '
+       git config commit.gpgsign "1true" &&
+       test_cmp_config 1true commit.gpgsign &&
+       test_must_fail git config --bool --get commit.gpgsign 2>actual &&
+       test_i18ngrep "bad boolean config value .1true. for .commit.gpgsign." actual
+'
+
 test_expect_success 'line number is reported correctly' '
        printf "[bool]\n\tvar\n" >invalid &&
        test_must_fail git config -f invalid --path bool.var 2>actual &&
index 3a527e3a8438a583bfd8704ad23f9ae2e48ac382..88b119a0a35dc6608a789b0415a97650c88b2e19 100755 (executable)
@@ -208,14 +208,14 @@ test_expect_success 'proper error on error in default config files' '
        echo "[" >>.git/config &&
        echo "fatal: bad config line 34 in file .git/config" >expect &&
        test_expect_code 128 test-tool config get_value foo.bar 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'proper error on error in custom config files' '
        echo "[" >>syntax-error &&
        echo "fatal: bad config line 1 in file syntax-error" >expect &&
        test_expect_code 128 test-tool config configset_get_value foo.bar syntax-error 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'check line errors for malformed values' '
index 359d8731c8f097cf015a304f1f4f01d3794c7b56..e31f65f381c9bdbdc3a9dc4c10a90e5051cf9bc6 100755 (executable)
@@ -374,7 +374,7 @@ test_expect_success 'Query "main@{May 25 2005}" (before history)' '
        echo "$C" >expect &&
        test_cmp expect o &&
        echo "warning: log for '\''main'\'' only goes back to $ed" >expect &&
-       test_i18ncmp expect e
+       test_cmp expect e
 '
 test_expect_success 'Query main@{2005-05-25} (before history)' '
        test_when_finished "rm -f o e" &&
@@ -382,7 +382,7 @@ test_expect_success 'Query main@{2005-05-25} (before history)' '
        echo "$C" >expect &&
        test_cmp expect o &&
        echo "warning: log for '\''main'\'' only goes back to $ed" >expect &&
-       test_i18ncmp expect e
+       test_cmp expect e
 '
 test_expect_success 'Query "main@{May 26 2005 23:31:59}" (1 second before history)' '
        test_when_finished "rm -f o e" &&
@@ -390,7 +390,7 @@ test_expect_success 'Query "main@{May 26 2005 23:31:59}" (1 second before histor
        echo "$C" >expect &&
        test_cmp expect o &&
        echo "warning: log for '\''main'\'' only goes back to $ed" >expect &&
-       test_i18ncmp expect e
+       test_cmp expect e
 '
 test_expect_success 'Query "main@{May 26 2005 23:32:00}" (exactly history start)' '
        test_when_finished "rm -f o e" &&
index 2d142e5535e5abfbeb2c8dbc68c5e9de9e2d92cd..8b51c4efc1359dacd1b760897e48c264b965f861 100755 (executable)
@@ -101,7 +101,7 @@ df_test() {
                printf "%s\n" "delete $delname" "create $addname $D"
        fi >commands &&
        test_must_fail git update-ref --stdin <commands 2>output.err &&
-       test_i18ncmp expected-err output.err &&
+       test_cmp expected-err output.err &&
        printf "%s\n" "$C $delref" >expected-refs &&
        git for-each-ref --format="%(objectname) %(refname)" $prefix/r >actual-refs &&
        test_cmp expected-refs actual-refs
index ecccaa063453642a24a8ce364a5c73262226f868..27b9080251a91b2f7031f3ccb6f9bafcbb9c5d22 100755 (executable)
@@ -158,6 +158,32 @@ test_expect_success 'reflog expire' '
        check_fsck "dangling commit $K"
 '
 
+test_expect_success '--stale-fix handles missing objects generously' '
+       git -c core.logAllRefUpdates=false fast-import --date-format=now <<-EOS &&
+       commit refs/heads/stale-fix
+       mark :1
+       committer Author <a@uth.or> now
+       data <<EOF
+       start stale fix
+       EOF
+       M 100644 inline file
+       data <<EOF
+       contents
+       EOF
+       commit refs/heads/stale-fix
+       committer Author <a@uth.or> now
+       data <<EOF
+       stale fix branch tip
+       EOF
+       from :1
+       EOS
+
+       parent_oid=$(git rev-parse stale-fix^) &&
+       test_when_finished "recover $parent_oid" &&
+       corrupt $parent_oid &&
+       git reflog expire --stale-fix
+'
+
 test_expect_success 'prune and fsck' '
 
        git prune &&
index 354902e514f964194828dbbe04d9d0d81dc50d85..b1839e08771d4162db11f2b6b6eabd33da10b275 100755 (executable)
@@ -125,7 +125,7 @@ test_expect_success 'push cannot create a badly named ref' '
        ! grep -e "broken\.\.\.ref" output
 '
 
-test_expect_failure C_LOCALE_OUTPUT 'push --mirror can delete badly named ref' '
+test_expect_failure 'push --mirror can delete badly named ref' '
        top=$(pwd) &&
        git init src &&
        git init dest &&
index a30fc5f74a95063da9d60703f09b4c6de88f5d52..5071ac63a5b51b89c973456211ce3aaac3587553 100755 (executable)
@@ -40,17 +40,13 @@ test_expect_success 'HEAD is part of refs, valid objects appear valid' '
 # specific corruption you test afterwards, lest a later test trip over
 # it.
 
-test_expect_success 'setup: helpers for corruption tests' '
-       sha1_file() {
-               remainder=${1#??} &&
-               firsttwo=${1%$remainder} &&
-               echo ".git/objects/$firsttwo/$remainder"
-       } &&
+sha1_file () {
+       git rev-parse --git-path objects/$(test_oid_to_path "$1")
+}
 
-       remove_object() {
-               rm "$(sha1_file "$1")"
-       }
-'
+remove_object () {
+       rm "$(sha1_file "$1")"
+}
 
 test_expect_success 'object with bad sha1' '
        sha=$(echo blob | git hash-object -w --stdin) &&
@@ -380,7 +376,7 @@ test_expect_success 'tag with incorrect tag name & missing tagger' '
        warning in tag $tag: badTagName: invalid '\''tag'\'' name: wrong name format
        warning in tag $tag: missingTaggerEntry: invalid format - expected '\''tagger'\'' line
        EOF
-       test_i18ncmp expect out
+       test_cmp expect out
 '
 
 test_expect_success 'tag with bad tagger' '
@@ -662,13 +658,15 @@ test_expect_success 'fsck --name-objects' '
        git init name-objects &&
        (
                cd name-objects &&
+               git config core.logAllRefUpdates false &&
                test_commit julius caesar.t &&
-               test_commit augustus &&
-               test_commit caesar &&
+               test_commit augustus44 &&
+               test_commit caesar  &&
                remove_object $(git rev-parse julius:caesar.t) &&
-               test_must_fail git fsck --name-objects >out &&
                tree=$(git rev-parse --verify julius:) &&
-               test_i18ngrep "$tree (refs/tags/julius:" out
+               git tag -d julius &&
+               test_must_fail git fsck --name-objects >out &&
+               test_i18ngrep "$tree (refs/tags/augustus44\\^:" out
        )
 '
 
@@ -806,7 +804,7 @@ test_expect_success 'fsck notices dangling objects' '
                git fsck >actual &&
                # the output order is non-deterministic, as it comes from a hash
                sort <actual >actual.sorted &&
-               test_i18ncmp expect actual.sorted
+               test_cmp expect actual.sorted
        )
 '
 
@@ -816,7 +814,7 @@ test_expect_success 'fsck --connectivity-only notices dangling objects' '
                git fsck --connectivity-only >actual &&
                # the output order is non-deterministic, as it comes from a hash
                sort <actual >actual.sorted &&
-               test_i18ncmp expect actual.sorted
+               test_cmp expect actual.sorted
        )
 '
 
index abdda360d5258daf5c97184c900ace674534537f..deae916707f0c1fea99c929ad0863b4aabf86457 100755 (executable)
@@ -243,4 +243,19 @@ test_expect_success 'showing the superproject correctly' '
        test_cmp expect out
 '
 
+# at least one external project depends on this behavior:
+test_expect_success 'rev-parse --since= unsqueezed ordering' '
+       x1=--since=1970-01-01T00:00:01Z &&
+       x2=--since=1970-01-01T00:00:02Z &&
+       x3=--since=1970-01-01T00:00:03Z &&
+       git rev-parse $x1 $x1 $x3 $x2 >actual &&
+       cat >expect <<-EOF &&
+       --max-age=1
+       --max-age=1
+       --max-age=3
+       --max-age=2
+       EOF
+       test_cmp expect actual
+'
+
 test_done
index a859abedf5820da7fd2a2457c6d19b213ff78784..b29563fc9973aad63a313cbb94f239d2365ee0d8 100755 (executable)
@@ -95,7 +95,7 @@ test_expect_success 'test --parseopt help output' '
 |EOF
 END_EXPECT
        test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'test --parseopt help output no switches' '
@@ -108,7 +108,7 @@ test_expect_success 'test --parseopt help output no switches' '
 |EOF
 END_EXPECT
        test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_no_switches &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'test --parseopt help output hidden switches' '
@@ -121,7 +121,7 @@ test_expect_success 'test --parseopt help output hidden switches' '
 |EOF
 END_EXPECT
        test_expect_code 129 git rev-parse --parseopt -- -h > output < optionspec_only_hidden_switches &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'test --parseopt help-all output hidden switches' '
@@ -136,7 +136,7 @@ test_expect_success 'test --parseopt help-all output hidden switches' '
 |EOF
 END_EXPECT
        test_expect_code 129 git rev-parse --parseopt -- --help-all > output < optionspec_only_hidden_switches &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'test --parseopt invalid switch help output' '
@@ -176,7 +176,7 @@ test_expect_success 'test --parseopt invalid switch help output' '
 |
 END_EXPECT
        test_expect_code 129 git rev-parse --parseopt -- --does-not-exist 1>/dev/null 2>output < optionspec &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'setup expect.1' "
index f6e6f23f7e6950ea43a7632eb2f0652afc1ed0ae..65a154a8a20f8df27a3f0eaee9ff9b65d5132639 100755 (executable)
@@ -15,7 +15,7 @@ test_did_you_mean ()
        fatal: path '$2$3' $4, but not ${5:-$SQ$3$SQ}
        hint: Did you mean '$1:$2$3'${2:+ aka $SQ$1:./$3$SQ}?
        EOF
-       test_i18ncmp expected error
+       test_cmp expected error
 }
 
 HASH_file=
index 73b4f34c6eee30a70098c950a5482756a9f95e61..c34714ffe3fbe5545da1d05b9a74cfc6113e43dd 100755 (executable)
@@ -172,7 +172,7 @@ test_expect_success 'branch@{u} error message when no upstream' '
        fatal: no upstream configured for branch ${SQ}non-tracking${SQ}
        EOF
        error_message non-tracking@{u} &&
-       test_i18ncmp expect error
+       test_cmp expect error
 '
 
 test_expect_success '@{u} error message when no upstream' '
@@ -180,7 +180,7 @@ test_expect_success '@{u} error message when no upstream' '
        fatal: no upstream configured for branch ${SQ}main${SQ}
        EOF
        test_must_fail git rev-parse --verify @{u} 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'branch@{u} error message with misspelt branch' '
@@ -188,7 +188,7 @@ test_expect_success 'branch@{u} error message with misspelt branch' '
        fatal: no such branch: ${SQ}no-such-branch${SQ}
        EOF
        error_message no-such-branch@{u} &&
-       test_i18ncmp expect error
+       test_cmp expect error
 '
 
 test_expect_success '@{u} error message when not on a branch' '
@@ -197,7 +197,7 @@ test_expect_success '@{u} error message when not on a branch' '
        EOF
        git checkout HEAD^0 &&
        test_must_fail git rev-parse --verify @{u} 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'branch@{u} error message if upstream branch not fetched' '
@@ -205,7 +205,7 @@ test_expect_success 'branch@{u} error message if upstream branch not fetched' '
        fatal: upstream branch ${SQ}refs/heads/side${SQ} not stored as a remote-tracking branch
        EOF
        error_message bad-upstream@{u} &&
-       test_i18ncmp expect error
+       test_cmp expect error
 '
 
 test_expect_success 'pull works when tracking a local branch' '
index fd2f7abf1c192eebaa98a3b7d3d22433cd523ae4..553a3f601ba7c76efa193d4a33823197c05281e7 100755 (executable)
@@ -221,7 +221,7 @@ test_expect_success 'setup' '
        rm -rf /.git &&
        echo "Initialized empty Git repository in /.git/" > expected &&
        git init > result &&
-       test_i18ncmp expected result
+       test_cmp expected result
 '
 
 test_vars 'auto gitdir, root' ".git" "/" ""
@@ -246,7 +246,7 @@ test_expect_success 'setup' '
        cd / &&
        echo "Initialized empty Git repository in /" > expected &&
        git init --bare > result &&
-       test_i18ncmp expected result
+       test_cmp expected result
 '
 
 test_vars 'auto gitdir, root' "." "" ""
index 242abbfa0bc2b3a1303665feee93e23f8c98a1f6..7891a6becf3a0b4092b2a3fd91ed6679e24cdd08 100755 (executable)
@@ -314,39 +314,39 @@ test_expect_success 'ambiguous short sha1 ref' '
        grep "refname.*${REF}.*ambiguous" err
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (raw)' '
+test_expect_success 'ambiguity errors are not repeated (raw)' '
        test_must_fail git rev-parse 00000 2>stderr &&
        grep "is ambiguous" stderr >errors &&
        test_line_count = 1 errors
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (treeish)' '
+test_expect_success 'ambiguity errors are not repeated (treeish)' '
        test_must_fail git rev-parse 00000:foo 2>stderr &&
        grep "is ambiguous" stderr >errors &&
        test_line_count = 1 errors
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity errors are not repeated (peel)' '
+test_expect_success 'ambiguity errors are not repeated (peel)' '
        test_must_fail git rev-parse 00000^{commit} 2>stderr &&
        grep "is ambiguous" stderr >errors &&
        test_line_count = 1 errors
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints' '
+test_expect_success 'ambiguity hints' '
        test_must_fail git rev-parse 000000000 2>stderr &&
        grep ^hint: stderr >hints &&
        # 16 candidates, plus one intro line
        test_line_count = 17 hints
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguity hints respect type' '
+test_expect_success 'ambiguity hints respect type' '
        test_must_fail git rev-parse 000000000^{commit} 2>stderr &&
        grep ^hint: stderr >hints &&
        # 5 commits, 1 tag (which is a committish), plus intro line
        test_line_count = 7 hints
 '
 
-test_expect_success C_LOCALE_OUTPUT 'failed type-selector still shows hint' '
+test_expect_success 'failed type-selector still shows hint' '
        # these two blobs share the same prefix "ee3d", but neither
        # will pass for a commit
        echo 851 | git hash-object --stdin -w &&
@@ -370,7 +370,7 @@ test_expect_success 'core.disambiguate does not override context' '
                git -c core.disambiguate=committish rev-parse $sha1^{tree}
 '
 
-test_expect_success C_LOCALE_OUTPUT 'ambiguous commits are printed by type first, then hash order' '
+test_expect_success 'ambiguous commits are printed by type first, then hash order' '
        test_must_fail git rev-parse 0000 2>stderr &&
        grep ^hint: stderr >hints &&
        grep 0000 hints >objects &&
index b7c31aa86a11ffbecd7c82a3ab3e920105caf71f..c9b9e718b89403012b8ce555b474698b8d567140 100755 (executable)
@@ -18,7 +18,7 @@ test_expect_success 'bogus GIT_INDEX_VERSION issues warning' '
                        warning: GIT_INDEX_VERSION set, but the value is invalid.
                        Using version Z
                EOF
-               test_i18ncmp expect.err actual.err
+               test_cmp expect.err actual.err
        )
 '
 
@@ -32,7 +32,7 @@ test_expect_success 'out of bounds GIT_INDEX_VERSION issues warning' '
                        warning: GIT_INDEX_VERSION set, but the value is invalid.
                        Using version Z
                EOF
-               test_i18ncmp expect.err actual.err
+               test_cmp expect.err actual.err
        )
 '
 
@@ -55,7 +55,7 @@ test_expect_success 'out of bounds index.version issues warning' '
                        warning: index.version set, but the value is invalid.
                        Using version Z
                EOF
-               test_i18ncmp expect.err actual.err
+               test_cmp expect.err actual.err
        )
 '
 
index 8e181dbf01c46a324b54471cc62e1c9291852079..7ff3edab05aec604b740608a29e54b4f257f8f8f 100755 (executable)
@@ -32,4 +32,27 @@ test_expect_success 'checkout-index reports errors (stdin)' '
        test_i18ngrep not.in.the.cache stderr
 '
 
+test_expect_success 'checkout-index --temp correctly reports error on missing blobs' '
+       test_when_finished git reset --hard &&
+       missing_blob=$(echo "no such blob here" | git hash-object --stdin) &&
+       cat >objs <<-EOF &&
+       100644 $missing_blob    file
+       120000 $missing_blob    symlink
+       EOF
+       git update-index --index-info <objs &&
+
+       test_must_fail git checkout-index --temp symlink file 2>stderr &&
+       test_i18ngrep "unable to read sha1 file of file ($missing_blob)" stderr &&
+       test_i18ngrep "unable to read sha1 file of symlink ($missing_blob)" stderr
+'
+
+test_expect_success 'checkout-index --temp correctly reports error for submodules' '
+       git init sub &&
+       test_commit -C sub file &&
+       git submodule add ./sub &&
+       git commit -m sub &&
+       test_must_fail git checkout-index --temp sub 2>stderr &&
+       test_i18ngrep "cannot create temporary submodule sub" stderr
+'
+
 test_done
index 5f761bc616ee5871055d6beed29e768b328f6827..93be1c0eae5ead7eeb4f37dab01dbc1c1fbacdba 100755 (executable)
@@ -150,7 +150,7 @@ test_expect_success 'checkout -b to @{-1} fails with the right branch name' '
        git checkout branch2 &&
        echo  >expect "fatal: A branch named '\''branch1'\'' already exists." &&
        test_must_fail git checkout -b @{-1} 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'checkout -B to an existing branch resets branch to HEAD' '
index b432b6427b62538580b5833188b0d211c748545e..bc46713a43e24193bae9a4be204455eb5c066501 100755 (executable)
@@ -163,7 +163,7 @@ test_expect_success 'tracking count is accurate after orphan check' '
        git config branch.child.merge refs/heads/main &&
        git checkout child^ &&
        git checkout child >stdout &&
-       test_i18ncmp expect stdout
+       test_cmp expect stdout
 '
 
 test_expect_success 'no advice given for explicit detached head state' '
@@ -237,15 +237,15 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
        sane_unset GIT_PRINT_SHA1_ELLIPSIS &&
        git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 1st_detach actual &&
+       test_cmp 1st_detach actual &&
 
        GIT_PRINT_SHA1_ELLIPSIS="no" git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 2nd_detach actual &&
+       test_cmp 2nd_detach actual &&
 
        GIT_PRINT_SHA1_ELLIPSIS= git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 3rd_detach actual &&
+       test_cmp 3rd_detach actual &&
 
        sane_unset GIT_PRINT_SHA1_ELLIPSIS &&
 
@@ -256,17 +256,17 @@ test_expect_success 'describe_detached_head prints no SHA-1 ellipsis when not as
        # Make no mention of the env var at all
        git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 1st_detach actual &&
+       test_cmp 1st_detach actual &&
 
        GIT_PRINT_SHA1_ELLIPSIS='nope' &&
        git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 2nd_detach actual &&
+       test_cmp 2nd_detach actual &&
 
        GIT_PRINT_SHA1_ELLIPSIS=nein &&
        git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 3rd_detach actual &&
+       test_cmp 3rd_detach actual &&
 
        true
 "
@@ -319,15 +319,15 @@ test_expect_success 'describe_detached_head does print SHA-1 ellipsis when asked
 
        GIT_PRINT_SHA1_ELLIPSIS=yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 1st_detach actual &&
+       test_cmp 1st_detach actual &&
 
        GIT_PRINT_SHA1_ELLIPSIS=Yes git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 2nd_detach actual &&
+       test_cmp 2nd_detach actual &&
 
        GIT_PRINT_SHA1_ELLIPSIS=YES git -c 'core.abbrev=12' checkout HEAD^ >actual 2>&1 &&
        check_detached &&
-       test_i18ncmp 3rd_detach actual &&
+       test_cmp 3rd_detach actual &&
 
        true
 "
index 7e2e7dd4ae5842bc49ccaf5dc116deb3b3d6b993..30666fc70d28264d036105659871a68323ce2460 100755 (executable)
@@ -9,6 +9,11 @@ test_description='skip-worktree bit test'
 
 sane_unset GIT_TEST_SPLIT_INDEX
 
+test_set_index_version () {
+    GIT_INDEX_VERSION="$1"
+    export GIT_INDEX_VERSION
+}
+
 test_set_index_version 3
 
 cat >expect.full <<EOF
index 7cb7a7038277fdf5e55e984f998d56324d7c62cd..45ca35d60ac5b750a635ef42f96275c4bb197692 100755 (executable)
@@ -135,7 +135,7 @@ test_expect_success 'add -n -u should not add but just report' '
        after=$(git ls-files -s check top) &&
 
        test "$before" = "$after" &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 
 '
 
index aff877590dfe4b98b9e8f89439b6ecd2b0af6516..a615d3b4838192af5acac8afd9a19e662553e409 100755 (executable)
@@ -23,7 +23,7 @@ test_expect_success 'prune files inside $GIT_DIR/worktrees' '
        cat >expect <<EOF &&
 Removing worktrees/abc: not a valid directory
 EOF
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
        ! test -f .git/worktrees/abc &&
        ! test -d .git/worktrees
 '
@@ -35,7 +35,7 @@ test_expect_success 'prune directories without gitdir' '
 Removing worktrees/def: gitdir file does not exist
 EOF
        git worktree prune --verbose >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
        ! test -d .git/worktrees/def &&
        ! test -d .git/worktrees
 '
index 42d35d9ae82f5e3084d1cc37512718565aa6c41a..fedcefe8de334f2925f0536b16fd405fecc52836 100755 (executable)
@@ -167,7 +167,7 @@ test_expect_success '"list" all worktrees --verbose with prunable' '
        rm -rf prunable &&
        git worktree list --verbose >out &&
        sed -n "s/  */ /g;/\/prunable  *[0-9a-f].*$/,/prunable: .*$/p" <out >actual &&
-       test_i18ncmp actual expect
+       test_cmp actual expect
 '
 
 test_expect_success 'bare repo setup' '
index 2ec69a8a266fa2438f960ef7d63d6517bbe61b78..727e9ae1a449ee4f105fe234b5041c6fbc397b45 100755 (executable)
@@ -46,7 +46,7 @@ test_expect_success 'ls-files -c' '
                ls ../x* >expect.out &&
                test_must_fail git ls-files -c --error-unmatch ../[xy]* >actual.out 2>actual.err &&
                test_cmp expect.out actual.out &&
-               test_i18ncmp expect.err actual.err
+               test_cmp expect.err actual.err
        )
 '
 
@@ -61,7 +61,7 @@ test_expect_success 'ls-files -o' '
                ls ../y* >expect.out &&
                test_must_fail git ls-files -o --error-unmatch ../[xy]* >actual.out 2>actual.err &&
                test_cmp expect.out actual.out &&
-               test_i18ncmp expect.err actual.err
+               test_cmp expect.err actual.err
        )
 '
 
index 00761e408015ddf60e1ce2cd5ab18032f79e3c68..cc4b10236e2c8335465185cff84105208b807aa9 100755 (executable)
@@ -695,7 +695,7 @@ test_expect_success 'deleting a symref' '
        git branch -d symref >actual &&
        test_path_is_file .git/refs/heads/target &&
        test_path_is_missing .git/refs/heads/symref &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'deleting a dangling symref' '
@@ -704,7 +704,7 @@ test_expect_success 'deleting a dangling symref' '
        echo "Deleted branch dangling-symref (was nowhere)." >expect &&
        git branch -d dangling-symref >actual &&
        test_path_is_missing .git/refs/heads/dangling-symref &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'deleting a self-referential symref' '
@@ -713,7 +713,7 @@ test_expect_success 'deleting a self-referential symref' '
        echo "Deleted branch self-reference (was refs/heads/self-reference)." >expect &&
        git branch -d self-reference >actual &&
        test_path_is_missing .git/refs/heads/self-reference &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'renaming a symref is not allowed' '
@@ -808,7 +808,7 @@ test_expect_success 'test deleting branch without config' '
        sha1=$(git rev-parse my7 | cut -c 1-7) &&
        echo "Deleted branch my7 (was $sha1)." >expect &&
        git branch -d my7 >actual 2>&1 &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'deleting currently checked out branch fails' '
@@ -843,7 +843,7 @@ test_expect_success 'branch from tag w/--track causes failure' '
 test_expect_success '--set-upstream-to fails on multiple branches' '
        echo "fatal: too many arguments to set new upstream" >expect &&
        test_must_fail git branch --set-upstream-to main a b c 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on detached HEAD' '
@@ -851,13 +851,13 @@ test_expect_success '--set-upstream-to fails on detached HEAD' '
        test_when_finished git checkout - &&
        echo "fatal: could not set upstream of HEAD to main when it does not point to any branch." >expect &&
        test_must_fail git branch --set-upstream-to main 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on a missing dst branch' '
        echo "fatal: branch '"'"'does-not-exist'"'"' does not exist" >expect &&
        test_must_fail git branch --set-upstream-to main does-not-exist 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on a missing src branch' '
@@ -868,7 +868,7 @@ test_expect_success '--set-upstream-to fails on a missing src branch' '
 test_expect_success '--set-upstream-to fails on a non-ref' '
        echo "fatal: Cannot setup tracking information; starting point '"'"'HEAD^{}'"'"' is not a branch." >expect &&
        test_must_fail git branch --set-upstream-to HEAD^{} 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success '--set-upstream-to fails on locked config' '
@@ -899,7 +899,7 @@ test_expect_success 'use --set-upstream-to modify a particular branch' '
 test_expect_success '--unset-upstream should fail if given a non-existent branch' '
        echo "fatal: Branch '"'"'i-dont-exist'"'"' has no upstream information" >expect &&
        test_must_fail git branch --unset-upstream i-dont-exist 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success '--unset-upstream should fail if config is locked' '
@@ -921,13 +921,13 @@ test_expect_success 'test --unset-upstream on HEAD' '
        # fail for a branch without upstream set
        echo "fatal: Branch '"'"'main'"'"' has no upstream information" >expect &&
        test_must_fail git branch --unset-upstream 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success '--unset-upstream should fail on multiple branches' '
        echo "fatal: too many arguments to unset upstream" >expect &&
        test_must_fail git branch --unset-upstream a b c 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success '--unset-upstream should fail on detached HEAD' '
@@ -935,7 +935,7 @@ test_expect_success '--unset-upstream should fail on detached HEAD' '
        test_when_finished git checkout - &&
        echo "fatal: could not unset upstream of HEAD when it does not point to any branch." >expect &&
        test_must_fail git branch --unset-upstream 2>err &&
-       test_i18ncmp expect err
+       test_cmp expect err
 '
 
 test_expect_success 'test --unset-upstream on a particular branch' '
@@ -957,7 +957,7 @@ test_expect_success '--set-upstream-to notices an error to set branch as own ups
        EOF
        test_expect_code 1 git config branch.my13.remote &&
        test_expect_code 1 git config branch.my13.merge &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 # Keep this test last, as it changes the current branch
index 578b5f48255deb3f1ef547a224a7c6169f2251fc..349a810cee11df36dda3c8ed4bbca3df58040001 100755 (executable)
@@ -264,7 +264,7 @@ test_expect_success 'branch --merged with --verbose' '
        * topic $(git rev-parse --short topic ) [ahead 1] foo
          zzz   $(git rev-parse --short zzz   ) second on main
        EOF
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_done
index b6fcd017afe9d9bdcb470071b849d69322ba5443..5325b9f67a00783974c34b4ac88431b903b3ccec 100755 (executable)
@@ -160,7 +160,7 @@ test_expect_success 'git branch shows detached HEAD properly' '
 EOF
        git checkout HEAD^0 &&
        git branch >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly after checkout --detach' '
@@ -173,7 +173,7 @@ test_expect_success 'git branch shows detached HEAD properly after checkout --de
 EOF
        git checkout --detach &&
        git branch >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly after moving' '
@@ -185,7 +185,7 @@ test_expect_success 'git branch shows detached HEAD properly after moving' '
 EOF
        git reset --hard HEAD^1 &&
        git branch >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly from tag' '
@@ -198,7 +198,7 @@ EOF
        git tag fromtag main &&
        git checkout fromtag &&
        git branch >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch shows detached HEAD properly after moving from tag' '
@@ -210,7 +210,7 @@ test_expect_success 'git branch shows detached HEAD properly after moving from t
 EOF
        git reset --hard HEAD^1 &&
        git branch >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch `--sort=[-]objectsize` option' '
@@ -221,7 +221,7 @@ test_expect_success 'git branch `--sort=[-]objectsize` option' '
          main
        EOF
        git branch --sort=objectsize >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        cat >expect <<-\EOF &&
        * (HEAD detached from fromtag)
@@ -230,7 +230,7 @@ test_expect_success 'git branch `--sort=[-]objectsize` option' '
          branch-two
        EOF
        git branch --sort=-objectsize >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch `--sort=[-]type` option' '
@@ -241,7 +241,7 @@ test_expect_success 'git branch `--sort=[-]type` option' '
          main
        EOF
        git branch --sort=type >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        cat >expect <<-\EOF &&
        * (HEAD detached from fromtag)
@@ -250,7 +250,7 @@ test_expect_success 'git branch `--sort=[-]type` option' '
          main
        EOF
        git branch --sort=-type >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch `--sort=[-]version:refname` option' '
@@ -261,7 +261,7 @@ test_expect_success 'git branch `--sort=[-]version:refname` option' '
          main
        EOF
        git branch --sort=version:refname >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        cat >expect <<-\EOF &&
        * (HEAD detached from fromtag)
@@ -270,7 +270,7 @@ test_expect_success 'git branch `--sort=[-]version:refname` option' '
          branch-one
        EOF
        git branch --sort=-version:refname >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git branch --points-at option' '
@@ -337,7 +337,7 @@ test_expect_success 'git branch --format option' '
        Refname is refs/heads/ref-to-remote
        EOF
        git branch --format="Refname is %(refname)" >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'worktree colors correct' '
@@ -355,7 +355,7 @@ test_expect_success 'worktree colors correct' '
        rm -r worktree_dir &&
        git worktree prune &&
        test_decode_color <actual.raw >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success "set up color tests" '
@@ -398,7 +398,7 @@ test_expect_success 'verbose output lists worktree path' '
        git branch -vv >actual &&
        rm -r worktree_dir &&
        git worktree prune &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_done
index ce24b5d735abc263be7c4125155a47663d1616cf..1b26c4c2ef9139bf2ac0312f49b6fd4d8ac8c4dd 100755 (executable)
@@ -153,6 +153,19 @@ test_expect_success 'simple A B C (unmodified)' '
        test_cmp expect actual
 '
 
+test_expect_success 'A^! and A^-<n> (unmodified)' '
+       git range-diff --no-color topic^! unmodified^-1 >actual &&
+       cat >expect <<-EOF &&
+       1:  $(test_oid t4) = 1:  $(test_oid u4) s/12/B/
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'A^{/..} is not mistaken for a range' '
+       test_must_fail git range-diff topic^.. topic^{/..} 2>error &&
+       test_i18ngrep "not a commit range" error
+'
+
 test_expect_success 'trivial reordering' '
        git range-diff --no-color main topic reordered >actual &&
        cat >expect <<-EOF &&
@@ -720,4 +733,19 @@ test_expect_success 'format-patch --range-diff with multiple notes' '
        test_cmp expect actual
 '
 
+test_expect_success '--left-only/--right-only' '
+       git switch --orphan left-right &&
+       test_commit first &&
+       test_commit unmatched &&
+       test_commit common &&
+       git switch -C left-right first &&
+       git cherry-pick common &&
+
+       git range-diff -s --left-only ...common >actual &&
+       head_oid=$(git rev-parse --short HEAD) &&
+       common_oid=$(git rev-parse --short common) &&
+       echo "1:  $head_oid = 2:  $common_oid common" >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 04de03cad0a7466106a125205742b5feea1b1e2f..f5bf16abcd8ce2aae5929129913cbb8927b06539 100755 (executable)
@@ -181,7 +181,7 @@ test_expect_success 'diffstat for rename quotes funny filename' '
        git diff-index -M -p $t0 >diff &&
        git apply --stat <diff >diffstat &&
        sed -e "s/|.*//" -e "s/ *\$//" <diffstat >current &&
-       test_i18ncmp expected current
+       test_cmp expected current
 '
 
 test_expect_success 'numstat for rename quotes funny filename' '
index 5e88a10f8d770ff53e05a4005bd4aa6d7b90918c..587b408063a8f705124fa44855f15cdd4c873a4c 100755 (executable)
@@ -113,7 +113,7 @@ test_expect_success 'rebase off of the previous branch using "-"' '
        test_cmp expect.forkpoint actual.forkpoint &&
        # the next one is dubious---we may want to say "-",
        # instead of @{-1}, in the message
-       test_i18ncmp expect.messages actual.messages
+       test_cmp expect.messages actual.messages
 '
 
 test_expect_success 'rebase a single mode change' '
index 1e738df81d574e2e68d343a67afae266443e39c6..66bcbbf9528cec9e372f39be80e23fa789aabbd2 100755 (executable)
@@ -86,7 +86,7 @@ test_expect_success 'rebase -i with empty todo list' '
                        git rebase -i HEAD^ >output 2>&1
        ) &&
        tail -n 1 output >actual &&  # Ignore output about changing todo list
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rebase -i with the exec command' '
@@ -101,7 +101,8 @@ test_expect_success 'rebase -i with the exec command' '
        ) &&
        test_path_is_file touch-one &&
        test_path_is_file touch-two &&
-       test_path_is_missing touch-three " (should have stopped before)" &&
+       # Missing because we should have stopped by now.
+       test_path_is_missing touch-three &&
        test_cmp_rev C HEAD &&
        git rebase --continue &&
        test_path_is_file touch-three &&
@@ -158,9 +159,9 @@ test_expect_success 'rebase -x with empty command fails' '
        test_when_finished "git rebase --abort ||:" &&
        test_must_fail env git rebase -x "" @ 2>actual &&
        test_write_lines "error: empty exec command" >expected &&
-       test_i18ncmp expected actual &&
+       test_cmp expected actual &&
        test_must_fail env git rebase -x " " @ 2>actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'rebase -x with newline in command fails' '
@@ -168,7 +169,7 @@ test_expect_success 'rebase -x with newline in command fails' '
        test_must_fail env git rebase -x "a${LF}b" @ 2>actual &&
        test_write_lines "error: exec commands cannot contain newlines" \
                         >expected &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'rebase -i with exec of inexistent command' '
@@ -450,7 +451,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
        grep "^ file1 | 2 +-$" output
 '
 
-test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '
+test_expect_success 'multi-squash only fires up editor once' '
        base=$(git rev-parse HEAD~4) &&
        (
                set_fake_editor &&
@@ -463,7 +464,7 @@ test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '
        test 1 = $(git show | grep ONCE | wc -l)
 '
 
-test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' '
+test_expect_success 'multi-fixup does not fire up editor' '
        git checkout -b multi-fixup E &&
        base=$(git rev-parse HEAD~4) &&
        (
@@ -514,7 +515,7 @@ test_expect_success 'commit message retained after conflict' '
        git branch -D conflict-squash
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '
+test_expect_success 'squash and fixup generate correct log messages' '
        cat >expect-squash-fixup <<-\EOF &&
        B
 
@@ -541,7 +542,7 @@ test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messa
        git branch -D squash-fixup
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '
+test_expect_success 'squash ignores comments' '
        git checkout -b skip-comments E &&
        base=$(git rev-parse HEAD~4) &&
        (
@@ -557,7 +558,7 @@ test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '
        git branch -D skip-comments
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' '
+test_expect_success 'squash ignores blank lines' '
        git checkout -b skip-blank-lines E &&
        base=$(git rev-parse HEAD~4) &&
        (
@@ -995,7 +996,7 @@ test_expect_success 'rebase -ix with several instances of --exec' '
        test_cmp expected actual
 '
 
-test_expect_success C_LOCALE_OUTPUT 'rebase -ix with --autosquash' '
+test_expect_success 'rebase -ix with --autosquash' '
        git reset --hard execute &&
        git checkout -b autosquash &&
        echo second >second.txt &&
@@ -1136,7 +1137,7 @@ test_expect_success 'rebase -i --root reword root when root has untracked file c
        test "$(git rev-list --count HEAD)" = 2
 '
 
-test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
+test_expect_success 'rebase --edit-todo does not work on non-interactive rebase' '
        git checkout reword-original-root-branch &&
        git reset --hard &&
        git checkout conflict-branch &&
@@ -1465,7 +1466,7 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = warn' '
                FAKE_LINES="1 2 3 4" git rebase -i --root 2>actual.2
        ) &&
        head -n4 actual.2 >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
        test D = $(git cat-file commit HEAD | sed -ne \$p)
 '
 
@@ -1489,7 +1490,7 @@ test_expect_success 'rebase -i respects rebase.missingCommitsCheck = error' '
                set_fake_editor &&
                test_must_fail env FAKE_LINES="1 2 4" \
                        git rebase -i --root 2>actual &&
-               test_i18ncmp expect actual &&
+               test_cmp expect actual &&
                cp .git/rebase-merge/git-rebase-todo.backup \
                        .git/rebase-merge/git-rebase-todo &&
                FAKE_LINES="1 2 drop 3 4 drop 5" git rebase --edit-todo
@@ -1535,11 +1536,11 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = wa
                cp .git/rebase-merge/git-rebase-todo.backup orig &&
                FAKE_LINES="2 3 4" git rebase --edit-todo 2>actual.2 &&
                head -n6 actual.2 >actual &&
-               test_i18ncmp expect actual &&
+               test_cmp expect actual &&
                cp orig .git/rebase-merge/git-rebase-todo &&
                FAKE_LINES="1 2 3 4" git rebase --edit-todo 2>actual.2 &&
                head -n4 actual.2 >actual &&
-               test_i18ncmp expect.3 actual &&
+               test_cmp expect.3 actual &&
                git rebase --continue 2>actual
        ) &&
        test D = $(git cat-file commit HEAD | sed -ne \$p) &&
@@ -1575,16 +1576,16 @@ test_expect_success 'rebase --edit-todo respects rebase.missingCommitsCheck = er
                cp .git/rebase-merge/git-rebase-todo.backup orig &&
                test_must_fail env FAKE_LINES="2 3 4" \
                        git rebase --edit-todo 2>actual &&
-               test_i18ncmp expect actual &&
+               test_cmp expect actual &&
                test_must_fail git rebase --continue 2>actual &&
-               test_i18ncmp expect.2 actual &&
+               test_cmp expect.2 actual &&
                test_must_fail git rebase --edit-todo &&
                cp orig .git/rebase-merge/git-rebase-todo &&
                test_must_fail env FAKE_LINES="1 2 3 4" \
                        git rebase --edit-todo 2>actual &&
-               test_i18ncmp expect.3 actual &&
+               test_cmp expect.3 actual &&
                test_must_fail git rebase --continue 2>actual &&
-               test_i18ncmp expect.3 actual &&
+               test_cmp expect.3 actual &&
                cp orig .git/rebase-merge/git-rebase-todo &&
                FAKE_LINES="1 2 3 4 drop 5" git rebase --edit-todo &&
                git rebase --continue 2>actual
index 36f169d7f15559c0e7cd129365e404ec5ad99b03..908016c2f883e393b0d5da97d6183c3edab6a035 100755 (executable)
@@ -306,23 +306,23 @@ test_auto_fixup_fixup () {
        fi
 }
 
-test_expect_success C_LOCALE_OUTPUT 'fixup! fixup!' '
+test_expect_success 'fixup! fixup!' '
        test_auto_fixup_fixup fixup fixup
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fixup! squash!' '
+test_expect_success 'fixup! squash!' '
        test_auto_fixup_fixup fixup squash
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash! squash!' '
+test_expect_success 'squash! squash!' '
        test_auto_fixup_fixup squash squash
 '
 
-test_expect_success C_LOCALE_OUTPUT 'squash! fixup!' '
+test_expect_success 'squash! fixup!' '
        test_auto_fixup_fixup squash fixup
 '
 
-test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' '
+test_expect_success 'autosquash with custom inst format' '
        git reset --hard base &&
        git config --add rebase.instructionFormat "[%an @ %ar] %s"  &&
        echo 2 >file1 &&
index 4caa014c71c442f181b2668a3690c4e6ce1806db..43fcb68f27e439b5542e82a85bc77e401adc6e98 100755 (executable)
@@ -110,7 +110,7 @@ testrebase () {
                fi &&
                create_expected_success_$suffix &&
                sed "$remove_progress_re" <actual >actual2 &&
-               test_i18ncmp expected actual2
+               test_cmp expected actual2
        '
 
        test_expect_success "rebase$type: dirty index, non-conflicting rebase" '
@@ -231,7 +231,7 @@ testrebase () {
                fi &&
                create_expected_failure_$suffix &&
                sed "$remove_progress_re" <actual >actual2 &&
-               test_i18ncmp expected actual2
+               test_cmp expected actual2
        '
 }
 
index 9198535874c9788df49b6ad71183b5372d44eacd..4581ae98b87cf1f05d018cfc07599e8e26d256da 100755 (executable)
@@ -74,10 +74,10 @@ test_expect_success 'cherry-pick --continue rejects --rerere-autoupdate' '
        git diff-files --quiet &&
        test_must_fail git cherry-pick --continue --rerere-autoupdate >actual 2>&1 &&
        echo "fatal: cherry-pick: --rerere-autoupdate cannot be used with --continue" >expect &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
        test_must_fail git cherry-pick --continue --no-rerere-autoupdate >actual 2>&1 &&
        echo "fatal: cherry-pick: --no-rerere-autoupdate cannot be used with --continue" >expect &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
        git cherry-pick --abort
 '
 
index 5f4564c830682223187f160b343a1a8604a49018..014001b8f325c0f1d8b140e7828332bb15610574 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success 'advice from failed cherry-pick' "
        EOF
        test_must_fail git cherry-pick picked 2>actual &&
 
-       test_i18ncmp expected actual
+       test_cmp expected actual
 "
 
 test_expect_success 'advice from failed cherry-pick --no-commit' "
@@ -73,7 +73,7 @@ test_expect_success 'advice from failed cherry-pick --no-commit' "
        EOF
        test_must_fail git cherry-pick --no-commit picked 2>actual &&
 
-       test_i18ncmp expected actual
+       test_cmp expected actual
 "
 
 test_expect_success 'failed cherry-pick sets CHERRY_PICK_HEAD' '
@@ -256,7 +256,7 @@ test_expect_success \
 
        test_must_fail git cherry-pick picked &&
 
-       test_i18ncmp expected .git/MERGE_MSG
+       test_cmp expected .git/MERGE_MSG
 '
 
 test_expect_success \
@@ -276,7 +276,7 @@ test_expect_success \
 
        test_must_fail git cherry-pick --cleanup=scissors picked &&
 
-       test_i18ncmp expected .git/MERGE_MSG
+       test_cmp expected .git/MERGE_MSG
 '
 
 test_expect_success 'failed cherry-pick describes conflict in work tree' '
@@ -465,7 +465,7 @@ test_expect_success \
        test_must_fail git revert picked &&
 
        sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success \
@@ -488,7 +488,7 @@ test_expect_success \
        test_must_fail git revert --cleanup=scissors picked &&
 
        sed "s/$OID_REGEX/OBJID/" .git/MERGE_MSG >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'failed cherry-pick does not forget -s' '
index ec7a2c9fcfa2f2636c428ed78b12050cfa733d46..e8375d1c970e3313302a2580d742f1e88732070c 100755 (executable)
@@ -86,7 +86,7 @@ test_expect_success 'output to keep user entertained during multi-pick' '
        git cherry-pick first..fourth >actual &&
        sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
        test_line_count -ge 3 actual.fuzzy &&
-       test_i18ncmp expected actual.fuzzy
+       test_cmp expected actual.fuzzy
 '
 
 test_expect_success 'cherry-pick --strategy resolve first..fourth works' '
@@ -123,7 +123,7 @@ test_expect_success 'output during multi-pick indicates merge strategy' '
        test_tick &&
        git cherry-pick --strategy resolve first..fourth >actual &&
        sed -e "s/$_x05[0-9a-f][0-9a-f]/OBJID/" <actual >actual.fuzzy &&
-       test_i18ncmp expected actual.fuzzy
+       test_cmp expected actual.fuzzy
 '
 
 test_expect_success 'cherry-pick --ff first..fourth works' '
index 5b94fdaa6713a2bcf7958d6630925a09fea05ed6..b76cb6de91d0dd67bb242fcad1bc88b4c8a4901f 100755 (executable)
@@ -170,7 +170,7 @@ test_expect_success 'check advice when we move HEAD by committing' '
        git commit -a &&
        test_path_is_missing .git/CHERRY_PICK_HEAD &&
        test_must_fail git cherry-pick --skip 2>advice &&
-       test_i18ncmp expect advice
+       test_cmp expect advice
 '
 
 test_expect_success 'selectively advise --skip while launching another sequence' '
@@ -182,7 +182,7 @@ test_expect_success 'selectively advise --skip while launching another sequence'
        EOF
        test_must_fail git cherry-pick picked..yetanotherpick &&
        test_must_fail git cherry-pick picked..yetanotherpick 2>advice &&
-       test_i18ncmp expect advice &&
+       test_cmp expect advice &&
        cat >expect <<-EOF &&
        error: cherry-pick is already in progress
        hint: try "git cherry-pick (--continue | --abort | --quit)"
@@ -190,7 +190,7 @@ test_expect_success 'selectively advise --skip while launching another sequence'
        EOF
        git reset --merge &&
        test_must_fail git cherry-pick picked..yetanotherpick 2>advice &&
-       test_i18ncmp expect advice
+       test_cmp expect advice
 '
 
 test_expect_success 'allow skipping commit but not abort for a new history' '
@@ -204,7 +204,7 @@ test_expect_success 'allow skipping commit but not abort for a new history' '
        test_must_fail git cherry-pick anotherpick &&
        test_must_fail git cherry-pick --abort 2>advice &&
        git cherry-pick --skip &&
-       test_i18ncmp expect advice
+       test_cmp expect advice
 '
 
 test_expect_success 'allow skipping stopped cherry-pick because of untracked file modifications' '
index dff12286699f21c9b6c235a45d8af1cecef95938..bb9ef35dac082e396d5cbcbdc83bc5394fb95a7c 100755 (executable)
@@ -243,7 +243,7 @@ test_expect_success 'refresh index before checking if it is up-to-date' '
        test_path_is_missing frotz/nitfol
 '
 
-test_expect_success 'choking "git rm" should not let it die with cruft' '
+choke_git_rm_setup() {
        git reset -q --hard &&
        test_when_finished "rm -f .git/index.lock && git reset -q --hard" &&
        i=0 &&
@@ -252,12 +252,24 @@ test_expect_success 'choking "git rm" should not let it die with cruft' '
        do
                echo "100644 $hash 0    some-file-$i"
                i=$(( $i + 1 ))
-       done | git update-index --index-info &&
+       done | git update-index --index-info
+}
+
+test_expect_success 'choking "git rm" should not let it die with cruft (induce SIGPIPE)' '
+       choke_git_rm_setup &&
        # git command is intentionally placed upstream of pipe to induce SIGPIPE
        git rm -n "some-file-*" | : &&
        test_path_is_missing .git/index.lock
 '
 
+
+test_expect_success !MINGW 'choking "git rm" should not let it die with cruft (induce and check SIGPIPE)' '
+       choke_git_rm_setup &&
+       OUT=$( ((trap "" PIPE; git rm -n "some-file-*"; echo $? 1>&3) | :) 3>&1 ) &&
+       test_match_signal 13 "$OUT" &&
+       test_path_is_missing .git/index.lock
+'
+
 test_expect_success 'Resolving by removal is not a warning-worthy event' '
        git reset -q --hard &&
        test_when_finished "rm -f .git/index.lock msg && git reset -q --hard" &&
@@ -442,7 +454,7 @@ test_expect_success 'rm issues a warning when section is not found in .gitmodule
        git add .gitmodules &&
        echo "warning: Could not find section in .gitmodules where path=submod" >expect.err &&
        git rm submod >actual 2>actual.err &&
-       test_i18ncmp expect.err actual.err &&
+       test_cmp expect.err actual.err &&
        test_path_is_missing submod &&
        test_path_is_missing submod/.git &&
        git status -s -uno >actual &&
@@ -812,7 +824,7 @@ test_expect_success 'rm files with different staged content' '
        echo content1 >foo.txt &&
        echo content1 >bar.txt &&
        test_must_fail git rm foo.txt bar.txt 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rm files with different staged content without hints' '
@@ -825,7 +837,7 @@ test_expect_success 'rm files with different staged content without hints' '
        echo content2 >foo.txt &&
        echo content2 >bar.txt &&
        test_must_fail git -c advice.rmhints=false rm foo.txt bar.txt 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rm file with local modification' '
@@ -837,7 +849,7 @@ test_expect_success 'rm file with local modification' '
        git commit -m "testing rm 3" &&
        echo content3 >foo.txt &&
        test_must_fail git rm foo.txt 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rm file with local modification without hints' '
@@ -847,7 +859,7 @@ test_expect_success 'rm file with local modification without hints' '
        EOF
        echo content4 >bar.txt &&
        test_must_fail git -c advice.rmhints=false rm bar.txt 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rm file with changes in the index' '
@@ -860,7 +872,7 @@ test_expect_success 'rm file with changes in the index' '
        echo content5 >foo.txt &&
        git add foo.txt &&
        test_must_fail git rm foo.txt 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rm file with changes in the index without hints' '
@@ -869,7 +881,7 @@ test_expect_success 'rm file with changes in the index without hints' '
            foo.txt
        EOF
        test_must_fail git -c advice.rmhints=false rm foo.txt 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rm files with two different errors' '
@@ -888,7 +900,7 @@ test_expect_success 'rm files with two different errors' '
        echo content6 >bar1.txt &&
        git add bar1.txt &&
        test_must_fail git rm bar1.txt foo1.txt 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'rm empty string should fail' '
index b7d4ba608cbc96998fdac714ebb53a0f53c46f9e..dda84004f96fa89c17f4226072d978a7a1f2c84c 100755 (executable)
@@ -304,7 +304,7 @@ test_expect_success 'error on a repository with no commits' '
        error: '"'empty/'"' does not have a commit checked out
        fatal: adding files failed
        EOF
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'git add --dry-run of existing changed file' "
@@ -320,7 +320,7 @@ test_expect_success 'git add --dry-run of non-existing file' "
 
 test_expect_success 'git add --dry-run of an existing file output' "
        echo \"fatal: pathspec 'ignored-file' did not match any files\" >expect &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 "
 
 cat >expect.err <<\EOF
@@ -339,8 +339,8 @@ test_expect_success 'git add --dry-run --ignore-missing of non-existing file' '
 '
 
 test_expect_success 'git add --dry-run --ignore-missing of non-existing file output' '
-       test_i18ncmp expect.out actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.out actual.out &&
+       test_cmp expect.err actual.err
 '
 
 test_expect_success 'git add empty string should fail' '
index b2f90997dbc0457b8d06563977e2d7f4b1fc345d..207714655f283971f791b4ab2426aa0d7b910080 100755 (executable)
@@ -370,7 +370,7 @@ test_expect_success 'setup expected' '
 '
 
 # Test splitting the first patch, then adding both
-test_expect_success C_LOCALE_OUTPUT 'add first line works' '
+test_expect_success 'add first line works' '
        git commit -am "clear local changes" &&
        git apply patch &&
        printf "%s\n" s y y | git add -p file 2>error |
@@ -974,7 +974,7 @@ test_expect_success 'show help from add--helper' '
        EOF
        test_write_lines h | force_color git add -i >actual.colored &&
        test_decode_color <actual.colored >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_done
index 86bfeb271ecb616d64cf483d8a46e33fa55ecc7b..60a666da595cf359b9132fb359960e43ea7e2f41 100755 (executable)
@@ -13,13 +13,11 @@ test_description='git mktag: tag object verify test'
 
 check_verify_failure () {
        test_expect_success "$1" "
-               test_must_fail env GIT_TEST_GETTEXT_POISON=false \
-                       git mktag <tag.sig 2>message &&
+               test_must_fail git mktag <tag.sig 2>message &&
                grep '$2' message &&
                if test '$3' != '--no-strict'
                then
-                       test_must_fail env GIT_TEST_GETTEXT_POISON=false \
-                               git mktag --no-strict <tag.sig 2>message.no-strict &&
+                       test_must_fail git mktag --no-strict <tag.sig 2>message.no-strict &&xb
                        grep '$2' message.no-strict
                fi
        "
@@ -443,11 +441,9 @@ test_expect_success 'invalid header entry config & fsck' '
        git -c fsck.extraHeaderEntry=ignore mktag --no-strict <tag.sig &&
 
        git fsck &&
-       env GIT_TEST_GETTEXT_POISON=false \
-               git -c fsck.extraHeaderEntry=warn fsck 2>err &&
+       git -c fsck.extraHeaderEntry=warn fsck 2>err &&
        grep "warning .*extraHeaderEntry:" err &&
-       test_must_fail env GIT_TEST_GETTEXT_POISON=false \
-               git -c fsck.extraHeaderEntry=error 2>err fsck &&
+       test_must_fail git -c fsck.extraHeaderEntry=error 2>err fsck &&
        grep "error .* extraHeaderEntry:" err
 '
 
index 84b039e5739378fd6ef1365d643304b862a7b95a..5f282ecf6175d381242b13178b9088a76cd28805 100755 (executable)
@@ -564,7 +564,7 @@ test_expect_success 'stash show format defaults to --stat' '
         1 file changed, 1 insertion(+)
        EOF
        git stash show ${STASH_ID} >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'stash show - stashes on stack, stash-like argument' '
@@ -792,7 +792,7 @@ test_expect_success 'apply: show same status as git status (relative to ./)' '
                git stash apply
        ) |
        sed -e 1d >actual && # drop "Saved..."
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<EOF
@@ -1114,7 +1114,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' '
        git stash push -p foo >actual &&
        echo "No local changes to save" >expect &&
        git reset --hard HEAD~ &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'push <pathspec>: show no changes when there are none' '
@@ -1124,7 +1124,7 @@ test_expect_success 'push <pathspec>: show no changes when there are none' '
        git stash push foo >actual &&
        echo "No local changes to save" >expect &&
        git reset --hard HEAD~ &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'push: <pathspec> not in the repository errors out' '
index f075c7f1f3165adc256e0018d26c34e8f7823b96..598b17f6d4de87f566f5f8d2cde2ac3e43b5c427 100755 (executable)
@@ -8,16 +8,16 @@ test_description='Test git stash --include-untracked'
 . ./test-lib.sh
 
 test_expect_success 'stash save --include-untracked some dirty working directory' '
-       echo 1 > file &&
+       echo 1 >file &&
        git add file &&
        test_tick &&
        git commit -m initial &&
-       echo 2 > file &&
+       echo 2 >file &&
        git add file &&
-       echo 3 > file &&
+       echo 3 >file &&
        test_tick &&
-       echo 1 > file2 &&
-       echo 1 > HEAD &&
+       echo 1 >file2 &&
+       echo 1 >HEAD &&
        mkdir untracked &&
        echo untracked >untracked/untracked &&
        git stash --include-untracked &&
@@ -25,48 +25,50 @@ test_expect_success 'stash save --include-untracked some dirty working directory
        git diff-index --cached --quiet HEAD
 '
 
-cat > expect <<EOF
-?? actual
-?? expect
-EOF
-
 test_expect_success 'stash save --include-untracked cleaned the untracked files' '
+       cat >expect <<-EOF &&
+       ?? actual
+       ?? expect
+       EOF
+
        git status --porcelain >actual &&
        test_cmp expect actual
 '
 
-tracked=$(git rev-parse --short $(echo 1 | git hash-object --stdin))
-untracked=$(git rev-parse --short $(echo untracked | git hash-object --stdin))
-cat > expect.diff <<EOF
-diff --git a/HEAD b/HEAD
-new file mode 100644
-index 0000000..$tracked
---- /dev/null
-+++ b/HEAD
-@@ -0,0 +1 @@
-+1
-diff --git a/file2 b/file2
-new file mode 100644
-index 0000000..$tracked
---- /dev/null
-+++ b/file2
-@@ -0,0 +1 @@
-+1
-diff --git a/untracked/untracked b/untracked/untracked
-new file mode 100644
-index 0000000..$untracked
---- /dev/null
-+++ b/untracked/untracked
-@@ -0,0 +1 @@
-+untracked
-EOF
-cat > expect.lstree <<EOF
-HEAD
-file2
-untracked
-EOF
-
 test_expect_success 'stash save --include-untracked stashed the untracked files' '
+       one_blob=$(echo 1 | git hash-object --stdin) &&
+       tracked=$(git rev-parse --short "$one_blob") &&
+       untracked_blob=$(echo untracked | git hash-object --stdin) &&
+       untracked=$(git rev-parse --short "$untracked_blob") &&
+       cat >expect.diff <<-EOF &&
+       diff --git a/HEAD b/HEAD
+       new file mode 100644
+       index 0000000..$tracked
+       --- /dev/null
+       +++ b/HEAD
+       @@ -0,0 +1 @@
+       +1
+       diff --git a/file2 b/file2
+       new file mode 100644
+       index 0000000..$tracked
+       --- /dev/null
+       +++ b/file2
+       @@ -0,0 +1 @@
+       +1
+       diff --git a/untracked/untracked b/untracked/untracked
+       new file mode 100644
+       index 0000000..$untracked
+       --- /dev/null
+       +++ b/untracked/untracked
+       @@ -0,0 +1 @@
+       +untracked
+       EOF
+       cat >expect.lstree <<-EOF &&
+       HEAD
+       file2
+       untracked
+       EOF
+
        test_path_is_missing file2 &&
        test_path_is_missing untracked &&
        test_path_is_missing HEAD &&
@@ -83,57 +85,64 @@ test_expect_success 'stash save --patch --all fails' '
        test_must_fail git stash --patch --all
 '
 
-git clean --force --quiet
+test_expect_success 'clean up untracked/untracked file to prepare for next tests' '
+       git clean --force --quiet
 
-cat > expect <<EOF
- M file
-?? HEAD
-?? actual
-?? expect
-?? file2
-?? untracked/
-EOF
+'
 
 test_expect_success 'stash pop after save --include-untracked leaves files untracked again' '
+       cat >expect <<-EOF &&
+        M file
+       ?? HEAD
+       ?? actual
+       ?? expect
+       ?? file2
+       ?? untracked/
+       EOF
+
        git stash pop &&
        git status --porcelain >actual &&
        test_cmp expect actual &&
-       test "1" = "$(cat file2)" &&
-       test untracked = "$(cat untracked/untracked)"
+       echo 1 >expect_file2 &&
+       test_cmp expect_file2 file2 &&
+       echo untracked >untracked_expect &&
+       test_cmp untracked_expect untracked/untracked
 '
 
-git clean --force --quiet -d
+test_expect_success 'clean up untracked/ directory to prepare for next tests' '
+       git clean --force --quiet -d
+'
 
 test_expect_success 'stash save -u dirty index' '
-       echo 4 > file3 &&
+       echo 4 >file3 &&
        git add file3 &&
        test_tick &&
        git stash -u
 '
 
-blob=$(git rev-parse --short $(echo 4 | git hash-object --stdin))
-cat > expect <<EOF
-diff --git a/file3 b/file3
-new file mode 100644
-index 0000000..$blob
---- /dev/null
-+++ b/file3
-@@ -0,0 +1 @@
-+4
-EOF
-
 test_expect_success 'stash save --include-untracked dirty index got stashed' '
+       four_blob=$(echo 4 | git hash-object --stdin) &&
+       blob=$(git rev-parse --short "$four_blob") &&
+       cat >expect <<-EOF &&
+       diff --git a/file3 b/file3
+       new file mode 100644
+       index 0000000..$blob
+       --- /dev/null
+       +++ b/file3
+       @@ -0,0 +1 @@
+       +4
+       EOF
+
        git stash pop --index &&
+       test_when_finished "git reset" &&
        git diff --cached >actual &&
        test_cmp expect actual
 '
 
-git reset > /dev/null
-
 # Must direct output somewhere where it won't be considered an untracked file
 test_expect_success 'stash save --include-untracked -q is quiet' '
-       echo 1 > file5 &&
-       git stash save --include-untracked --quiet > .git/stash-output.out 2>&1 &&
+       echo 1 >file5 &&
+       git stash save --include-untracked --quiet >.git/stash-output.out 2>&1 &&
        test_line_count = 0 .git/stash-output.out &&
        rm -f .git/stash-output.out
 '
@@ -141,35 +150,34 @@ test_expect_success 'stash save --include-untracked -q is quiet' '
 test_expect_success 'stash save --include-untracked removed files' '
        rm -f file &&
        git stash save --include-untracked &&
-       echo 1 > expect &&
+       echo 1 >expect &&
+       test_when_finished "rm -f expect" &&
        test_cmp expect file
 '
 
-rm -f expect
-
 test_expect_success 'stash save --include-untracked removed files got stashed' '
        git stash pop &&
        test_path_is_missing file
 '
 
-cat > .gitignore <<EOF
-.gitignore
-ignored
-ignored.d/
-EOF
-
 test_expect_success 'stash save --include-untracked respects .gitignore' '
-       echo ignored > ignored &&
+       cat >.gitignore <<-EOF &&
+       .gitignore
+       ignored
+       ignored.d/
+       EOF
+
+       echo ignored >ignored &&
        mkdir ignored.d &&
        echo ignored >ignored.d/untracked &&
        git stash -u &&
-       test -s ignored &&
-       test -s ignored.d/untracked &&
-       test -s .gitignore
+       test_file_not_empty ignored &&
+       test_file_not_empty ignored.d/untracked &&
+       test_file_not_empty .gitignore
 '
 
 test_expect_success 'stash save -u can stash with only untracked files different' '
-       echo 4 > file4 &&
+       echo 4 >file4 &&
        git stash -u &&
        test_path_is_missing file4
 '
@@ -183,9 +191,9 @@ test_expect_success 'stash save --all does not respect .gitignore' '
 
 test_expect_success 'stash save --all is stash poppable' '
        git stash pop &&
-       test -s ignored &&
-       test -s ignored.d/untracked &&
-       test -s .gitignore
+       test_file_not_empty ignored &&
+       test_file_not_empty ignored.d/untracked &&
+       test_file_not_empty .gitignore
 '
 
 test_expect_success 'stash push --include-untracked with pathspec' '
@@ -214,17 +222,17 @@ test_expect_success 'stash push with $IFS character' '
        test_path_is_file bar
 '
 
-cat > .gitignore <<EOF
-ignored
-ignored.d/*
-EOF
-
 test_expect_success 'stash previously ignored file' '
+       cat >.gitignore <<-EOF &&
+       ignored
+       ignored.d/*
+       EOF
+
        git reset HEAD &&
        git add .gitignore &&
        git commit -m "Add .gitignore" &&
        >ignored.d/foo &&
-       echo "!ignored.d/foo" >> .gitignore &&
+       echo "!ignored.d/foo" >>.gitignore &&
        git stash save --include-untracked &&
        test_path_is_missing ignored.d/foo &&
        git stash pop &&
@@ -280,7 +288,7 @@ test_expect_success 'stash -u -- <ignored> leaves ignored file alone' '
 test_expect_success 'stash -u -- <non-existent> shows no changes when there are none' '
        git stash push -u -- non-existent >actual &&
        echo "No local changes to save" >expect &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'stash -u with globs' '
index a0b9208ce83dfdd359b99aec105bea0ccd30a4c2..898267a6bd1c8d1320c8e808707ba079c39933fe 100755 (executable)
@@ -194,6 +194,22 @@ test_expect_failure 'handle existing decomposed filenames' '
        test_must_be_empty untracked
 '
 
+test_expect_success "unicode decomposed: git restore -p . " '
+       DIRNAMEPWD=dir.Odiarnfc &&
+       DIRNAMEINREPO=dir.$Adiarnfc &&
+       export DIRNAMEPWD DIRNAMEINREPO &&
+       git init "$DIRNAMEPWD" &&
+       (
+               cd "$DIRNAMEPWD" &&
+               mkdir "$DIRNAMEINREPO" &&
+               cd "$DIRNAMEINREPO" &&
+               echo "Initial" >file &&
+               git add file &&
+               echo "More stuff" >>file &&
+               echo y | git restore -p .
+       )
+'
+
 # Test if the global core.precomposeunicode stops autosensing
 # Must be the last test case
 test_expect_success "respect git config --global core.precomposeunicode" '
index e5116a76a1cc660370e1f5e958f87c2844a95f83..cce334981e19ea9b38a4ae204531c66bb8a57dc3 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test built-in diff output engine.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 echo >path0 'Line 1
 Line 2
index c16486a9d41a125610e6280a79e958ebbb792653..2f9700742aa2fb93385fc7fa83f23f2c8640b31d 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test rename detection in diff engine.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success 'setup' '
        cat >path0 <<-\EOF &&
index df2accb6555d4202c789b8ffb13ae3943268586d..db07ff3eb1968d606176f44ac2428899de78c6eb 100755 (executable)
@@ -7,11 +7,11 @@ test_description='More rename detection
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     tree=$(git write-tree) &&
@@ -99,7 +99,7 @@ test_expect_success \
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 # tree has COPYING and rezrov.  work tree has COPYING and COPYING.1,
index 6e562c80d12f9f58353c8f6444c0d7a0737dbae4..3d495e37bb1916741be13afb7664c56b465a7310 100755 (executable)
@@ -10,7 +10,7 @@ copy of symbolic links, but should not produce rename/copy followed
 by an edit for them.
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success SYMLINKS \
     'prepare reference tree' \
index d18a80493c2274d19a6f779bd1c67c50b63a6290..86479061325b67038e31bc13a7948d9b0b0f9323 100755 (executable)
@@ -6,10 +6,10 @@
 test_description='Same rename detection as t4003 but testing diff-raw.'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success 'setup reference tree' '
-       cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+       cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
        echo frotz >rezrov &&
        git update-index --add COPYING rezrov &&
        tree=$(git write-tree) &&
@@ -64,7 +64,7 @@ test_expect_success 'validate output from rename/copy detection (#2)' '
 # nows how to say Copy.
 
 test_expect_success 'validate output from rename/copy detection (#3)' '
-       cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+       cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
        git update-index --add --remove COPYING COPYING.1 &&
 
        cat <<-EOF >expected &&
index 03489aff14ea72921f66605eeac3ccddd694e923..275ce5fa15be18de8a5badbb49ac617eee538bf0 100755 (executable)
@@ -39,13 +39,13 @@ test_expect_success '--stat output after text chmod' '
         1 file changed, 0 insertions(+), 0 deletions(-)
        EOF
        git diff HEAD --stat >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success '--shortstat output after text chmod' '
        tail -n 1 <expect >expect.short &&
        git diff HEAD --shortstat >actual &&
-       test_i18ncmp expect.short actual
+       test_cmp expect.short actual
 '
 
 test_expect_success '--stat output after binary chmod' '
@@ -56,13 +56,13 @@ test_expect_success '--stat output after binary chmod' '
         2 files changed, 0 insertions(+), 0 deletions(-)
        EOF
        git diff HEAD --stat >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success '--shortstat output after binary chmod' '
        tail -n 1 <expect >expect.short &&
        git diff HEAD --shortstat >actual &&
-       test_i18ncmp expect.short actual
+       test_cmp expect.short actual
 '
 
 test_done
index b187b7f6c66b8ade9d0ef73aa346a9205df153bc..cbb9c62f535e807d13d72008088b108101c6ec57 100755 (executable)
@@ -7,17 +7,17 @@ test_description='Rename interaction with pathspec.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success 'prepare reference tree' '
        mkdir path0 path1 &&
-       cp "$TEST_DIRECTORY"/diff-lib/COPYING path0/COPYING &&
+       cp "$TEST_DIRECTORY"/lib-diff/COPYING path0/COPYING &&
        git update-index --add path0/COPYING &&
        tree=$(git write-tree) &&
        echo $tree
 '
 
-blob=$(git hash-object "$TEST_DIRECTORY/diff-lib/COPYING")
+blob=$(git hash-object "$TEST_DIRECTORY/lib-diff/COPYING")
 test_expect_success 'prepare work tree' '
        cp path0/COPYING path1/COPYING &&
        git update-index --add --remove path0/COPYING path1/COPYING
index b1ccd4102e09e27529eb09e873c2b4c2467a40db..2299f27511bf29fce131a890e0d8e9c6361d8a3c 100755 (executable)
@@ -22,11 +22,11 @@ With -B, this should be detected as two complete rewrites.
 Further, with -B and -M together, these should turn into two renames.
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success setup '
-       cat "$TEST_DIRECTORY"/diff-lib/README >file0 &&
-       cat "$TEST_DIRECTORY"/diff-lib/COPYING >file1 &&
+       cat "$TEST_DIRECTORY"/lib-diff/README >file0 &&
+       cat "$TEST_DIRECTORY"/lib-diff/COPYING >file1 &&
        blob0_id=$(git hash-object file0) &&
        blob1_id=$(git hash-object file1) &&
        git update-index --add file0 file1 &&
index b63bdf031f5186a9c482bbd08a9d95bd3beb1906..b1da807f1693147cf9a0d5fffeb1173921f08e57 100755 (executable)
@@ -7,11 +7,11 @@ test_description='Same rename detection as t4003 but testing diff-raw -z.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     'prepare reference tree' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
     orig=$(git hash-object COPYING) &&
@@ -81,7 +81,7 @@ test_expect_success \
 
 test_expect_success \
     'prepare work tree once again' \
-    'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
+    'cat "$TEST_DIRECTORY"/lib-diff/COPYING >COPYING &&
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -z -C --find-copies-harder $tree >current
index 65cc703c659b8941f1458e4f4b0b6d88dc5622d1..1bbced79ece861e2329663b09e8aead3daa983e0 100755 (executable)
@@ -10,7 +10,7 @@ Prepare:
         path1/file1
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 test_expect_success \
     setup \
index 717034bb50b57f3edc5d19989f0b178e912e08e6..5a25c259fe333912ba1614c2e645e03e5bdd6e02 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test diff of symlinks.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 # Print the short OID of a symlink with the given name.
 symlink_oid () {
index 6579c81216a9b2e7dc6f2457cc46003dd905aa30..33ff588ebca03807da91c4a786df76c03fb9bff7 100755 (executable)
@@ -34,19 +34,19 @@ EOF
 test_expect_success 'apply --stat output for binary file change' '
        git diff >diff &&
        git apply --stat --summary <diff >current &&
-       test_i18ncmp expected current
+       test_cmp expected current
 '
 
 test_expect_success 'diff --shortstat output for binary file change' '
        tail -n 1 expected >expect &&
        git diff --shortstat >current &&
-       test_i18ncmp expect current
+       test_cmp expect current
 '
 
 test_expect_success 'diff --shortstat output for binary file change only' '
        echo " 1 file changed, 0 insertions(+), 0 deletions(-)" >expected &&
        git diff --shortstat -- b >current &&
-       test_i18ncmp expected current
+       test_cmp expected current
 '
 
 test_expect_success 'apply --numstat notices binary file change' '
@@ -63,7 +63,7 @@ test_expect_success 'apply --numstat understands diff --binary format' '
 
 # apply needs to be able to skip the binary material correctly
 # in order to report the line number of a corrupt patch.
-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
+test_expect_success 'apply detecting corrupt patch correctly' '
        git diff >output &&
        sed -e "s/-CIT/xCIT/" <output >broken &&
        test_must_fail git apply --stat --summary broken 2>detected &&
@@ -73,7 +73,7 @@ test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
        test "$detected" = xCIT
 '
 
-test_expect_success C_LOCALE_OUTPUT 'apply detecting corrupt patch correctly' '
+test_expect_success 'apply detecting corrupt patch correctly' '
        git diff --binary | sed -e "s/-CIT/xCIT/" >broken &&
        test_must_fail git apply --stat --summary broken 2>detected &&
        detected=$(cat detected) &&
index ce6aa3914fe74f037eb2451a88e36803594b0e78..6cca8b84a6bf3f2098d47ab56bb9f8da0293dff5 100755 (executable)
@@ -9,7 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success setup '
 
@@ -222,7 +222,7 @@ do
                        process_diffs "$expect" >expect &&
                        case $cmd in
                        *format-patch* | *-stat*)
-                               test_i18ncmp expect actual;;
+                               test_cmp expect actual;;
                        *)
                                test_cmp expect actual;;
                        esac &&
index 66630c8413d5008caa53a2e3c36c652e61937cc5..cdd3154e702674007cef1e1a368cad0c5476c060 100755 (executable)
@@ -888,11 +888,11 @@ echo "fatal: --check does not make sense" >expect.check
 
 test_expect_success 'options no longer allowed for format-patch' '
        test_must_fail git format-patch --name-only 2>output &&
-       test_i18ncmp expect.name-only output &&
+       test_cmp expect.name-only output &&
        test_must_fail git format-patch --name-status 2>output &&
-       test_i18ncmp expect.name-status output &&
+       test_cmp expect.name-status output &&
        test_must_fail git format-patch --check 2>output &&
-       test_i18ncmp expect.check output
+       test_cmp expect.check output
 '
 
 test_expect_success 'format-patch --numstat should produce a patch' '
index 8c574221b27445938694163f90f90ce337bfc4e2..2c13b62d3c654807b85307748c1ec52b6f4155ff 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test special whitespace in diff engine.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success "Ray Lehtiniemi's example" '
        cat <<-\EOF >x &&
index 9c48e5c2c99ad1eafc343a2cf6b9eeda54f644d4..876271d6826cca4fea73e8cdb459e6983db5b209 100755 (executable)
@@ -82,7 +82,7 @@ test_expect_success 'git diff --stat -M HEAD' '
         7 files changed, 0 insertions(+), 0 deletions(-)
        EOF
        git diff --stat -M HEAD >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_done
index 894a11b224d29d7a9956f6d701e7a666d5c2f94a..94ef77e1dfedc28656d78c80b689eca6806a7b0c 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='difference in submodules'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 test_expect_success setup '
        test_tick &&
index 4cb9f0e523d2218566554cdccd9c2e3929f3d05b..c906320b60dcea80369c6c4ef1f6af70295713cd 100755 (executable)
@@ -139,7 +139,7 @@ EOF
 test_expect_success 'diffstat does not run textconv' '
        echo file diff=fail >.gitattributes &&
        git diff --stat HEAD^ HEAD >actual &&
-       test_i18ncmp expect.stat actual &&
+       test_cmp expect.stat actual &&
 
        head -n1 <expect.stat >expect.line1 &&
        head -n1 <actual >actual.line1 &&
index 0c8fb39cedaa31f6f586895cd57a878158624f13..56f1e62a97bff51505956e672b023d4f681aaf1d 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='word diff colors'
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 cat >pre.simple <<-\EOF
        h(4)
index 09ad491a593cc4c1381492e33f09f1a1ff31bd2b..aeac203c424905be395d5ef6d43f2b606dc3bb1d 100755 (executable)
@@ -6,7 +6,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 setup_helper () {
        one=$1 branch=$2 side=$3 &&
index 7be1de736d86c90ac9c36ff68c0e8f4535e9bafd..61ba5f707fbd419aa3625eeedc39210f9d6d59ac 100755 (executable)
@@ -61,7 +61,7 @@ check_stat () {
        EOF
        test_expect_success "--stat $*" "
                git -C '$dir' diff --stat $* HEAD^ >actual &&
-               test_i18ncmp expected actual
+               test_cmp expected actual
        "
 }
 
index a34121740a4ab58e8abd16b3a00df5f44d2f1648..53061b104ecc1af2eebb9d79fa2af5655236471d 100755 (executable)
@@ -25,7 +25,7 @@ test_expect_success 'mode-only change show as a 0-line change' '
         4 files changed, 2 insertions(+)
        EOF
        git diff --stat --stat-count=2 HEAD >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'binary changes do not count in lines' '
@@ -40,7 +40,7 @@ test_expect_success 'binary changes do not count in lines' '
         3 files changed, 2 insertions(+)
        EOF
        git diff --stat --stat-count=2 >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'exclude unmerged entries from total file count' '
@@ -62,7 +62,7 @@ test_expect_success 'exclude unmerged entries from total file count' '
         3 files changed, 3 insertions(+)
        EOF
        git diff --stat --stat-count=2 >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_done
index bcf7493740a5157bbeba205eebdba83f41dc0380..7750b87ca16ab39aa965caa4fe71b462d899adc7 100755 (executable)
@@ -7,7 +7,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 # Compare two diff outputs. Ignore "index" lines, because we don't
 # care about SHA-1s or file modes.
index 744b8e51beab59c78807e0622f10bf421b3142ec..9b433de83630774206fb89dfae1a4396264390cc 100755 (executable)
@@ -17,13 +17,13 @@ do
        test_expect_success "$title" '
                git apply --stat --summary \
                        <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" >current &&
-               test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+               test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
        '
 
        test_expect_success "$title with recount" '
                sed -e "$UNC" <"$TEST_DIRECTORY/t4100/t-apply-$num.patch" |
                git apply --recount --stat --summary >current &&
-               test_i18ncmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
+               test_cmp "$TEST_DIRECTORY"/t4100/t-apply-$num.expect current
        '
 done <<\EOF
 rename
index 99987515dc2dde28b8654ac4ccda2071ddeffc5b..2aaaa0d7dedf68934e8d5b4c6610f1a859acbb29 100755 (executable)
@@ -906,7 +906,7 @@ test_expect_success 'am empty-file does not infloop' '
        test_tick &&
        test_must_fail git am empty-file 2>actual &&
        echo Patch format detection failed. >expected &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'am --message-id really adds the message id' '
index 8ea22d1bcbb8340d32fe87f299a77c98362c3797..b7c3861407d026e7064dbab0237bfa709859c462 100755 (executable)
@@ -61,7 +61,7 @@ test_expect_success '--no-quiet overrides --quiet' '
        # Applying side2 will be quiet.
        git am --no-quiet --continue >out &&
        echo "Applying: side1" >expected &&
-       test_i18ncmp expected out
+       test_cmp expected out
 '
 
 test_expect_success '--signoff overrides --no-signoff' '
index 621f9962d51ea862af3692d2a9fd3fc358bbab0f..93caf9a46d9ce7e729fb805629b611296efd972f 100755 (executable)
@@ -889,4 +889,47 @@ test_expect_success 'empty syntax: setup' '
        test_cmp expect actual
 '
 
+test_expect_success 'set up mailmap location tests' '
+       git init --bare loc-bare &&
+       git --git-dir=loc-bare --work-tree=. commit \
+               --allow-empty -m foo --author="Orig <orig@example.com>" &&
+       echo "New <new@example.com> <orig@example.com>" >loc-bare/.mailmap
+'
+
+test_expect_success 'bare repo with --work-tree finds mailmap at top-level' '
+       git -C loc-bare --work-tree=. log -1 --format=%aE >actual &&
+       echo new@example.com >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'bare repo does not look in current directory' '
+       git -C loc-bare log -1 --format=%aE >actual &&
+       echo orig@example.com >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'non-git shortlog respects mailmap in current dir' '
+       git --git-dir=loc-bare log -1 >input &&
+       nongit cp "$TRASH_DIRECTORY/loc-bare/.mailmap" . &&
+       nongit git shortlog -s <input >actual &&
+       echo "     1    New" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'shortlog on stdin respects mailmap from repo' '
+       cp loc-bare/.mailmap . &&
+       git shortlog -s <input >actual &&
+       echo "     1    New" >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'find top-level mailmap from subdir' '
+       git clone loc-bare loc-wt &&
+       cp loc-bare/.mailmap loc-wt &&
+       mkdir loc-wt/subdir &&
+       git -C loc-wt/subdir log -1 --format=%aE >actual &&
+       echo new@example.com >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 749bc1431ac26660a477ad33364feba62c426956..85432b80ff52a7014be3c3912c38163e989adc70 100755 (executable)
@@ -123,10 +123,10 @@ test_expect_success 'NUL separation with --stat' '
        stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
        printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n" >expected &&
        git log -z --stat --pretty="format:%s" >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
-test_expect_failure C_LOCALE_OUTPUT 'NUL termination with --stat' '
+test_expect_failure 'NUL termination with --stat' '
        stat0_part=$(git diff --stat HEAD^ HEAD) &&
        stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
        printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected &&
index ad29e65fcba6be9a6b27954d5dcda7efe31d3018..4871a5dc92f0d586715728d667eefcf8c7644e00 100755 (executable)
@@ -7,7 +7,7 @@ test_description='Test --follow should always find copies hard in git log.
 
 '
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh
+. "$TEST_DIRECTORY"/lib-diff.sh
 
 echo >path0 'Line 1
 Line 2
index 5e10136e9a239fda6526da7f84df3ee49936a6eb..7f0c1dcc0f0bfbfd746648f4fee1b7588a8f31b8 100755 (executable)
@@ -31,13 +31,8 @@ test_expect_success '"git log :/a -- " should not be ambiguous' '
 test_expect_success '"git log :/detached -- " should find a commit only in HEAD' '
        test_when_finished "git checkout main" &&
        git checkout --detach &&
-       # Must manually call `test_tick` instead of using `test_commit`,
-       # because the latter additionally creates a tag, which would make
-       # the commit reachable not only via HEAD.
-       test_tick &&
-       git commit --allow-empty -m detached &&
-       test_tick &&
-       git commit --allow-empty -m something-else &&
+       test_commit --no-tag detached &&
+       test_commit --no-tag something-else &&
        git log :/detached --
 '
 
index 0f16c4b9d5270c3d3e710de0d58e483516188e2a..50f206db55043ff3481b4db4b23937b01bc43cab 100755 (executable)
@@ -43,11 +43,11 @@ test_expect_success 'setup test - repo, commits, commit graph, log outputs' '
 '
 
 graph_read_expect () {
-       NUM_CHUNKS=5
+       NUM_CHUNKS=6
        cat >expect <<- EOF
        header: 43475048 1 $(test_oid oid_version) $NUM_CHUNKS 0
        num_commits: $1
-       chunks: oid_fanout oid_lookup commit_metadata bloom_indexes bloom_data
+       chunks: oid_fanout oid_lookup commit_metadata generation_data bloom_indexes bloom_data
        EOF
        test-tool read-graph >actual &&
        test_cmp expect actual
index daf01c309d03203026d27f2ff978974053e8eeee..54be7da1611212143d843d175bf48f73bb83b417 100755 (executable)
@@ -60,7 +60,7 @@ test_expect_success 'try to apply corrupted patch' '
        test_must_fail git -c advice.amWorkDir=false am bad-patch.diff 2>actual &&
        echo "error: git diff header lacks filename information (line 4)" >expected &&
        test_path_is_file f &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success "NUL in commit message's body" '
index 3ebb0d3b6520b7813dd7a1cc20409d2ed779c9e1..7204799a0b52a027cf57a0c83cd9818f69a4ad8b 100755 (executable)
@@ -431,15 +431,33 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
        test_cmp expect actual
 '
 
-test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future (2^34 - 1) commit' '
+       rm -f .git/index &&
+       echo foo >file &&
+       git add file &&
+       GIT_COMMITTER_DATE="@17179869183 +0000" \
+               git commit -m "tempori parendum"
+'
+
+test_expect_success TIME_IS_64BIT 'generate tar with far-future mtime' '
+       git archive HEAD >future.tar
+'
+
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
+       echo 2514 >expect &&
+       tar_info future.tar | cut -d" " -f2 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success TIME_IS_64BIT 'set up repository with far-far-future (2^36 + 1) commit' '
        rm -f .git/index &&
        echo content >file &&
        git add file &&
-       GIT_COMMITTER_DATE="@68719476737 +0000" \
+       GIT_TEST_COMMIT_GRAPH=0 GIT_COMMITTER_DATE="@68719476737 +0000" \
                git commit -m "tempori parendum"
 '
 
-test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with far-far-future mtime' '
        git archive HEAD >future.tar
 '
 
index 3e7b23cb32c581b88473b596036517a38c51c9fd..2d32d0ed122277089966b44f015b5f8ff6765e1c 100755 (executable)
@@ -153,7 +153,8 @@ test_expect_success ZIPINFO 'zip archive with many entries' '
 
        # check the number of entries in the ZIP file directory
        expr 65536 + 256 >expect &&
-       "$ZIPINFO" many.zip | head -2 | sed -n "2s/.* //p" >actual &&
+       "$ZIPINFO" -h many.zip >zipinfo &&
+       sed -n "2s/.* //p" <zipinfo >actual &&
        test_cmp expect actual
 '
 
index db4e15fd5965d9863f379ad9a6a14041852a64bc..cb67bac1c47487f451c2113c6b6fca60cb438bfd 100755 (executable)
@@ -223,14 +223,14 @@ test_expect_success 'pull request format' '
                git request-pull initial "$downstream_url" tags/full >../request
        ) &&
        <request sed -nf fuzz.sed >request.fuzzy &&
-       test_i18ncmp expect request.fuzzy &&
+       test_cmp expect request.fuzzy &&
 
        (
                cd local &&
                git request-pull initial "$downstream_url" tags/full:refs/tags/full
        ) >request &&
        sed -nf fuzz.sed <request >request.fuzzy &&
-       test_i18ncmp expect request.fuzzy &&
+       test_cmp expect request.fuzzy &&
 
        (
                cd local &&
index 392201cabdfece945104c6cc8bde3e87f3edd99f..d586fdc7a955daf6ddd9590b707e4dee1aefc82c 100755 (executable)
@@ -427,7 +427,7 @@ test_expect_success 'index-pack --strict <pack> works in non-repo' '
        test_path_is_file foo.idx
 '
 
-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.threads=N warns when no pthreads' '
+test_expect_success !PTHREADS 'index-pack --threads=N or pack.threads=N warns when no pthreads' '
        test_must_fail git index-pack --threads=2 2>err &&
        grep ^warning: err >warnings &&
        test_line_count = 1 warnings &&
@@ -445,7 +445,7 @@ test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.th
        grep -F "no threads support, ignoring pack.threads" err
 '
 
-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '
+test_expect_success !PTHREADS 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '
        git pack-objects --threads=2 --stdout --all </dev/null >/dev/null 2>err &&
        grep ^warning: err >warnings &&
        test_line_count = 1 warnings &&
index 5ba76031090ef3fb27b6e861e5766dfa7ae3c427..40b9f632441446d5042100d2797e9dd80b17f643 100755 (executable)
@@ -5,6 +5,8 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=master
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bundle.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
 
 objpath () {
        echo ".git/objects/$(echo "$1" | sed -e 's|\(..\)|\1/|')"
index 0f06c40eb13f31d8f06962dd1c88c5ff5810618f..a8c1bc0f66b0da238af0cf1051bb7cfafbf27db8 100755 (executable)
@@ -84,14 +84,14 @@ test_expect_success 'packing produces a long delta' '
        pack=$(git pack-objects --all --window=0 </dev/null pack) &&
        echo 9 >expect &&
        max_chain pack-$pack.pack >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success '--depth limits depth' '
        pack=$(git pack-objects --all --depth=5 </dev/null pack) &&
        echo 5 >expect &&
        max_chain pack-$pack.pack >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_done
index 2ed0c1544da120c10db3afedb656429f12a45613..fa2ba93fe806e6404b9cf6ffeefa705bb96fa730 100755 (executable)
@@ -76,7 +76,7 @@ graph_git_behavior 'no graph' full commits/3 commits/1
 graph_read_expect() {
        OPTIONAL=""
        NUM_CHUNKS=3
-       if test ! -z $2
+       if test ! -z "$2"
        then
                OPTIONAL=" $2"
                NUM_CHUNKS=$((3 + $(echo "$2" | wc -w)))
@@ -103,14 +103,14 @@ test_expect_success 'exit with correct error on bad input to --stdin-commits' '
        # valid commit and tree OID
        git rev-parse HEAD HEAD^{tree} >in &&
        git commit-graph write --stdin-commits <in &&
-       graph_read_expect 3
+       graph_read_expect 3 generation_data
 '
 
 test_expect_success 'write graph' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "3"
+       graph_read_expect "3" generation_data
 '
 
 test_expect_success POSIXPERM 'write graph has correct permissions' '
@@ -219,7 +219,7 @@ test_expect_success 'write graph with merges' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "10" "extra_edges"
+       graph_read_expect "10" "generation_data extra_edges"
 '
 
 graph_git_behavior 'merge 1 vs 2' full merge/1 merge/2
@@ -254,7 +254,7 @@ test_expect_success 'write graph with new commit' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "extra_edges"
+       graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'full graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -264,7 +264,7 @@ test_expect_success 'write graph with nothing new' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "extra_edges"
+       graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'cleared graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -274,7 +274,7 @@ test_expect_success 'build graph from latest pack with closure' '
        cd "$TRASH_DIRECTORY/full" &&
        cat new-idx | git commit-graph write --stdin-packs &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "9" "extra_edges"
+       graph_read_expect "9" "generation_data extra_edges"
 '
 
 graph_git_behavior 'graph from pack, commit 8 vs merge 1' full commits/8 merge/1
@@ -287,7 +287,7 @@ test_expect_success 'build graph from commits with closure' '
        git rev-parse merge/1 >>commits-in &&
        cat commits-in | git commit-graph write --stdin-commits &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "6"
+       graph_read_expect "6" "generation_data"
 '
 
 graph_git_behavior 'graph from commits, commit 8 vs merge 1' full commits/8 merge/1
@@ -297,7 +297,7 @@ test_expect_success 'build graph from commits with append' '
        cd "$TRASH_DIRECTORY/full" &&
        git rev-parse merge/3 | git commit-graph write --stdin-commits --append &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "10" "extra_edges"
+       graph_read_expect "10" "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -307,7 +307,7 @@ test_expect_success 'build graph using --reachable' '
        cd "$TRASH_DIRECTORY/full" &&
        git commit-graph write --reachable &&
        test_path_is_file $objdir/info/commit-graph &&
-       graph_read_expect "11" "extra_edges"
+       graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'append graph, commit 8 vs merge 1' full commits/8 merge/1
@@ -328,7 +328,7 @@ test_expect_success 'write graph in bare repo' '
        cd "$TRASH_DIRECTORY/bare" &&
        git commit-graph write &&
        test_path_is_file $baredir/info/commit-graph &&
-       graph_read_expect "11" "extra_edges"
+       graph_read_expect "11" "generation_data extra_edges"
 '
 
 graph_git_behavior 'bare repo with graph, commit 8 vs merge 1' bare commits/8 merge/1
@@ -446,6 +446,27 @@ test_expect_success 'warn on improper hash version' '
        )
 '
 
+test_expect_success 'lower layers have overflow chunk' '
+       cd "$TRASH_DIRECTORY/full" &&
+       UNIX_EPOCH_ZERO="@0 +0000" &&
+       FUTURE_DATE="@2147483646 +0000" &&
+       rm -f .git/objects/info/commit-graph &&
+       test_commit --date "$FUTURE_DATE" future-1 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" old-1 &&
+       git commit-graph write --reachable &&
+       test_commit --date "$FUTURE_DATE" future-2 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" old-2 &&
+       git commit-graph write --reachable --split=no-merge &&
+       test_commit extra &&
+       git commit-graph write --reachable --split=no-merge &&
+       git commit-graph write --reachable &&
+       graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
+       mv .git/objects/info/commit-graph commit-graph-upgraded &&
+       git commit-graph write --reachable &&
+       graph_read_expect 16 "generation_data generation_data_overflow extra_edges" &&
+       test_cmp .git/objects/info/commit-graph commit-graph-upgraded
+'
+
 # the verify tests below expect the commit-graph to contain
 # exactly the commits reachable from the commits/8 branch.
 # If the file changes the set of commits in the list, then the
@@ -454,8 +475,9 @@ test_expect_success 'warn on improper hash version' '
 
 test_expect_success 'git commit-graph verify' '
        cd "$TRASH_DIRECTORY/full" &&
-       git rev-parse commits/8 | git commit-graph write --stdin-commits &&
-       git commit-graph verify >output
+       git rev-parse commits/8 | GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --stdin-commits &&
+       git commit-graph verify >output &&
+       graph_read_expect 9 extra_edges
 '
 
 NUM_COMMITS=9
@@ -529,7 +551,7 @@ corrupt_graph_and_verify() {
        zero_pos=${4:-${orig_size}} &&
        printf "$data" | dd of="$objdir/info/commit-graph" bs=1 seek="$pos" conv=notrunc &&
        dd of="$objdir/info/commit-graph" bs=1 seek="$zero_pos" if=/dev/null &&
-       generate_zero_bytes $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
+       test-tool genzeros $(($orig_size - $zero_pos)) >>"$objdir/info/commit-graph" &&
        corrupt_graph_verify "$grepstr"
 
 }
@@ -741,4 +763,56 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
        )
 '
 
+# We test the overflow-related code with the following repo history:
+#
+#               4:F - 5:N - 6:U
+#              /                \
+# 1:U - 2:N - 3:U                M:N
+#              \                /
+#               7:N - 8:F - 9:N
+#
+# Here the commits denoted by U have committer date of zero seconds
+# since Unix epoch, the commits denoted by N have committer date
+# starting from 1112354055 seconds since Unix epoch (default committer
+# date for the test suite), and the commits denoted by F have committer
+# date of (2 ^ 31 - 2) seconds since Unix epoch.
+#
+# The largest offset observed is 2 ^ 31, just large enough to overflow.
+#
+
+test_expect_success 'set up and verify repo with generation data overflow chunk' '
+       objdir=".git/objects" &&
+       UNIX_EPOCH_ZERO="@0 +0000" &&
+       FUTURE_DATE="@2147483646 +0000" &&
+       test_oid_cache <<-EOF &&
+       oid_version sha1:1
+       oid_version sha256:2
+       EOF
+       cd "$TRASH_DIRECTORY" &&
+       mkdir repo &&
+       cd repo &&
+       git init &&
+       test_commit --date "$UNIX_EPOCH_ZERO" 1 &&
+       test_commit 2 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" 3 &&
+       git commit-graph write --reachable &&
+       graph_read_expect 3 generation_data &&
+       test_commit --date "$FUTURE_DATE" 4 &&
+       test_commit 5 &&
+       test_commit --date "$UNIX_EPOCH_ZERO" 6 &&
+       git branch left &&
+       git reset --hard 3 &&
+       test_commit 7 &&
+       test_commit --date "$FUTURE_DATE" 8 &&
+       test_commit 9 &&
+       git branch right &&
+       git reset --hard 3 &&
+       test_merge M left right &&
+       git commit-graph write --reachable &&
+       graph_read_expect 10 "generation_data generation_data_overflow" &&
+       git commit-graph verify
+'
+
+graph_git_behavior 'generation data overflow chunk repo' repo left right
+
 test_done
index 297de502a94fdfc255e9e3de4db801e2f15d02f2..2fc3aadbd116a88a01d4cdc5ba1c46b2215a54a7 100755 (executable)
@@ -710,8 +710,9 @@ test_expect_success 'expire respects .keep files' '
                PACKA=$(ls .git/objects/pack/a-pack*\.pack | sed s/\.pack\$//) &&
                touch $PACKA.keep &&
                git multi-pack-index expire &&
-               ls -S .git/objects/pack/a-pack* | grep $PACKA >a-pack-files &&
-               test_line_count = 3 a-pack-files &&
+               test_path_is_file $PACKA.idx &&
+               test_path_is_file $PACKA.keep &&
+               test_path_is_file $PACKA.pack &&
                test-tool read-midx .git/objects | grep idx >midx-list &&
                test_line_count = 2 midx-list
        )
index 4d3842b83b9456362a08842ebc10614d02f0943d..8e90f3423b8f23397d1a7dd86405215d1d8502fd 100755 (executable)
@@ -13,11 +13,11 @@ test_expect_success 'setup repo' '
        infodir=".git/objects/info" &&
        graphdir="$infodir/commit-graphs" &&
        test_oid_cache <<-EOM
-       shallow sha1:1760
-       shallow sha256:2064
+       shallow sha1:2132
+       shallow sha256:2436
 
-       base sha1:1376
-       base sha256:1496
+       base sha1:1408
+       base sha256:1528
 
        oid_version sha1:1
        oid_version sha256:2
@@ -31,9 +31,9 @@ graph_read_expect() {
                NUM_BASE=$2
        fi
        cat >expect <<- EOF
-       header: 43475048 1 $(test_oid oid_version) 3 $NUM_BASE
+       header: 43475048 1 $(test_oid oid_version) 4 $NUM_BASE
        num_commits: $1
-       chunks: oid_fanout oid_lookup commit_metadata
+       chunks: oid_fanout oid_lookup commit_metadata generation_data
        EOF
        test-tool read-graph >output &&
        test_cmp expect output
@@ -453,4 +453,185 @@ test_expect_success 'prevent regression for duplicate commits across layers' '
        git -C dup commit-graph verify
 '
 
+NUM_FIRST_LAYER_COMMITS=64
+NUM_SECOND_LAYER_COMMITS=16
+NUM_THIRD_LAYER_COMMITS=7
+NUM_FOURTH_LAYER_COMMITS=8
+NUM_FIFTH_LAYER_COMMITS=16
+SECOND_LAYER_SEQUENCE_START=$(($NUM_FIRST_LAYER_COMMITS + 1))
+SECOND_LAYER_SEQUENCE_END=$(($SECOND_LAYER_SEQUENCE_START + $NUM_SECOND_LAYER_COMMITS - 1))
+THIRD_LAYER_SEQUENCE_START=$(($SECOND_LAYER_SEQUENCE_END + 1))
+THIRD_LAYER_SEQUENCE_END=$(($THIRD_LAYER_SEQUENCE_START + $NUM_THIRD_LAYER_COMMITS - 1))
+FOURTH_LAYER_SEQUENCE_START=$(($THIRD_LAYER_SEQUENCE_END + 1))
+FOURTH_LAYER_SEQUENCE_END=$(($FOURTH_LAYER_SEQUENCE_START + $NUM_FOURTH_LAYER_COMMITS - 1))
+FIFTH_LAYER_SEQUENCE_START=$(($FOURTH_LAYER_SEQUENCE_END + 1))
+FIFTH_LAYER_SEQUENCE_END=$(($FIFTH_LAYER_SEQUENCE_START + $NUM_FIFTH_LAYER_COMMITS - 1))
+
+# Current split graph chain:
+#
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'setup repo for mixed generation commit-graph-chain' '
+       graphdir=".git/objects/info/commit-graphs" &&
+       test_oid_cache <<-EOF &&
+       oid_version sha1:1
+       oid_version sha256:2
+       EOF
+       git init mixed &&
+       (
+               cd mixed &&
+               git config core.commitGraph true &&
+               git config gc.writeCommitGraph false &&
+               for i in $(test_seq $NUM_FIRST_LAYER_COMMITS)
+               do
+                       test_commit $i &&
+                       git branch commits/$i || return 1
+               done &&
+               git commit-graph write --reachable --split &&
+               graph_read_expect $NUM_FIRST_LAYER_COMMITS &&
+               test_line_count = 1 $graphdir/commit-graph-chain &&
+               for i in $(test_seq $SECOND_LAYER_SEQUENCE_START $SECOND_LAYER_SEQUENCE_END)
+               do
+                       test_commit $i &&
+                       git branch commits/$i || return 1
+               done &&
+               GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable --split=no-merge &&
+               test_line_count = 2 $graphdir/commit-graph-chain &&
+               test-tool read-graph >output &&
+               cat >expect <<-EOF &&
+               header: 43475048 1 $(test_oid oid_version) 4 1
+               num_commits: $NUM_SECOND_LAYER_COMMITS
+               chunks: oid_fanout oid_lookup commit_metadata
+               EOF
+               test_cmp expect output &&
+               git commit-graph verify &&
+               cat $graphdir/commit-graph-chain
+       )
+'
+
+# The new layer will be added without generation data chunk as it was not
+# present on the layer underneath it.
+#
+#      7 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'do not write generation data chunk if not present on existing tip' '
+       git clone mixed mixed-no-gdat &&
+       (
+               cd mixed-no-gdat &&
+               for i in $(test_seq $THIRD_LAYER_SEQUENCE_START $THIRD_LAYER_SEQUENCE_END)
+               do
+                       test_commit $i &&
+                       git branch commits/$i || return 1
+               done &&
+               git commit-graph write --reachable --split=no-merge &&
+               test_line_count = 3 $graphdir/commit-graph-chain &&
+               test-tool read-graph >output &&
+               cat >expect <<-EOF &&
+               header: 43475048 1 $(test_oid oid_version) 4 2
+               num_commits: $NUM_THIRD_LAYER_COMMITS
+               chunks: oid_fanout oid_lookup commit_metadata
+               EOF
+               test_cmp expect output &&
+               git commit-graph verify
+       )
+'
+
+# Number of commits in each layer of the split-commit graph before merge:
+#
+#      8 commits (No GDAT)
+# ------------------------
+#      7 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+# The top two layers are merged and do not have generation data chunk as layer below them does
+# not have generation data chunk.
+#
+#     15 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'do not write generation data chunk if the topmost remaining layer does not have generation data chunk' '
+       git clone mixed-no-gdat mixed-merge-no-gdat &&
+       (
+               cd mixed-merge-no-gdat &&
+               for i in $(test_seq $FOURTH_LAYER_SEQUENCE_START $FOURTH_LAYER_SEQUENCE_END)
+               do
+                       test_commit $i &&
+                       git branch commits/$i || return 1
+               done &&
+               git commit-graph write --reachable --split --size-multiple 1 &&
+               test_line_count = 3 $graphdir/commit-graph-chain &&
+               test-tool read-graph >output &&
+               cat >expect <<-EOF &&
+               header: 43475048 1 $(test_oid oid_version) 4 2
+               num_commits: $(($NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS))
+               chunks: oid_fanout oid_lookup commit_metadata
+               EOF
+               test_cmp expect output &&
+               git commit-graph verify
+       )
+'
+
+# Number of commits in each layer of the split-commit graph before merge:
+#
+#     16 commits (No GDAT)
+# ------------------------
+#     15 commits (No GDAT)
+# ------------------------
+#     16 commits (No GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+# The top three layers are merged and has generation data chunk as the topmost remaining layer
+# has generation data chunk.
+#
+#     47 commits (GDAT)
+# ------------------------
+#     64 commits (GDAT)
+#
+test_expect_success 'write generation data chunk if topmost remaining layer has generation data chunk' '
+       git clone mixed-merge-no-gdat mixed-merge-gdat &&
+       (
+               cd mixed-merge-gdat &&
+               for i in $(test_seq $FIFTH_LAYER_SEQUENCE_START $FIFTH_LAYER_SEQUENCE_END)
+               do
+                       test_commit $i &&
+                       git branch commits/$i || return 1
+               done &&
+               git commit-graph write --reachable --split --size-multiple 1 &&
+               test_line_count = 2 $graphdir/commit-graph-chain &&
+               test-tool read-graph >output &&
+               cat >expect <<-EOF &&
+               header: 43475048 1 $(test_oid oid_version) 5 1
+               num_commits: $(($NUM_SECOND_LAYER_COMMITS + $NUM_THIRD_LAYER_COMMITS + $NUM_FOURTH_LAYER_COMMITS + $NUM_FIFTH_LAYER_COMMITS))
+               chunks: oid_fanout oid_lookup commit_metadata generation_data
+               EOF
+               test_cmp expect output
+       )
+'
+
+test_expect_success 'write generation data chunk when commit-graph chain is replaced' '
+       git clone mixed mixed-replace &&
+       (
+               cd mixed-replace &&
+               git commit-graph write --reachable --split=replace &&
+               test_path_is_file $graphdir/commit-graph-chain &&
+               test_line_count = 1 $graphdir/commit-graph-chain &&
+               verify_chain_files_exist $graphdir &&
+               graph_read_expect $(($NUM_FIRST_LAYER_COMMITS + $NUM_SECOND_LAYER_COMMITS)) &&
+               git commit-graph verify
+       )
+'
+
 test_done
diff --git a/t/t5325-reverse-index.sh b/t/t5325-reverse-index.sh
new file mode 100755 (executable)
index 0000000..da453f6
--- /dev/null
@@ -0,0 +1,120 @@
+#!/bin/sh
+
+test_description='on-disk reverse index'
+. ./test-lib.sh
+
+# The below tests want control over the 'pack.writeReverseIndex' setting
+# themselves to assert various combinations of it with other options.
+sane_unset GIT_TEST_WRITE_REV_INDEX
+
+packdir=.git/objects/pack
+
+test_expect_success 'setup' '
+       test_commit base &&
+
+       pack=$(git pack-objects --all $packdir/pack) &&
+       rev=$packdir/pack-$pack.rev &&
+
+       test_path_is_missing $rev
+'
+
+test_index_pack () {
+       rm -f $rev &&
+       conf=$1 &&
+       shift &&
+       # remove the index since Windows won't overwrite an existing file
+       rm $packdir/pack-$pack.idx &&
+       git -c pack.writeReverseIndex=$conf index-pack "$@" \
+               $packdir/pack-$pack.pack
+}
+
+test_expect_success 'index-pack with pack.writeReverseIndex' '
+       test_index_pack "" &&
+       test_path_is_missing $rev &&
+
+       test_index_pack false &&
+       test_path_is_missing $rev &&
+
+       test_index_pack true &&
+       test_path_is_file $rev
+'
+
+test_expect_success 'index-pack with --[no-]rev-index' '
+       for conf in "" true false
+       do
+               test_index_pack "$conf" --rev-index &&
+               test_path_exists $rev &&
+
+               test_index_pack "$conf" --no-rev-index &&
+               test_path_is_missing $rev
+       done
+'
+
+test_expect_success 'index-pack can verify reverse indexes' '
+       test_when_finished "rm -f $rev" &&
+       test_index_pack true &&
+
+       test_path_is_file $rev &&
+       git index-pack --rev-index --verify $packdir/pack-$pack.pack &&
+
+       # Intentionally corrupt the reverse index.
+       chmod u+w $rev &&
+       printf "xxxx" | dd of=$rev bs=1 count=4 conv=notrunc &&
+
+       test_must_fail git index-pack --rev-index --verify \
+               $packdir/pack-$pack.pack 2>err &&
+       grep "validation error" err
+'
+
+test_expect_success 'index-pack infers reverse index name with -o' '
+       git index-pack --rev-index -o other.idx $packdir/pack-$pack.pack &&
+       test_path_is_file other.idx &&
+       test_path_is_file other.rev
+'
+
+test_expect_success 'pack-objects respects pack.writeReverseIndex' '
+       test_when_finished "rm -fr pack-1-*" &&
+
+       git -c pack.writeReverseIndex= pack-objects --all pack-1 &&
+       test_path_is_missing pack-1-*.rev &&
+
+       git -c pack.writeReverseIndex=false pack-objects --all pack-1 &&
+       test_path_is_missing pack-1-*.rev &&
+
+       git -c pack.writeReverseIndex=true pack-objects --all pack-1 &&
+       test_path_is_file pack-1-*.rev
+'
+
+test_expect_success 'reverse index is not generated when available on disk' '
+       test_index_pack true &&
+       test_path_is_file $rev &&
+
+       git rev-parse HEAD >tip &&
+       GIT_TEST_REV_INDEX_DIE_IN_MEMORY=1 git cat-file \
+               --batch-check="%(objectsize:disk)" <tip
+'
+
+test_expect_success 'revindex in-memory vs on-disk' '
+       git init repo &&
+       test_when_finished "rm -fr repo" &&
+       (
+               cd repo &&
+
+               test_commit commit &&
+
+               git rev-list --objects --no-object-names --all >objects &&
+
+               git -c pack.writeReverseIndex=false repack -ad &&
+               test_path_is_missing $packdir/pack-*.rev &&
+               git cat-file --batch-check="%(objectsize:disk) %(objectname)" \
+                       <objects >in-core &&
+
+               git -c pack.writeReverseIndex=true repack -ad &&
+               test_path_is_file $packdir/pack-*.rev &&
+               git cat-file --batch-check="%(objectsize:disk) %(objectname)" \
+                       <objects >on-disk &&
+
+               test_cmp on-disk in-core
+       )
+'
+test_done
index 13107fcdb65c4c1d3274130a3f9fd86a6ffc0a3d..6694858e187a8a1607abbf69100706f56f0cd24b 100644 (file)
@@ -58,3 +58,18 @@ filter_out_user_friendly_and_stable_output () {
        make_user_friendly_and_stable_output |
                sed -n ${1+"$@"}
 }
+
+test_cmp_refs () {
+       indir=
+       if test "$1" = "-C"
+       then
+               shift
+               indir="$1"
+               shift
+       fi
+       indir=${indir:+"$indir"/}
+       cat >show-ref.expect &&
+       git ${indir:+ -C "$indir"} show-ref >show-ref.pristine &&
+       make_user_friendly_and_stable_output <show-ref.pristine >show-ref.filtered &&
+       test_cmp show-ref.expect show-ref.filtered
+}
index cb431a9c91d7182f192f5bf8f5a5d6b6da2f0a5b..1233a46eac5f8ca9606c6de370e0a2e32136ae72 100644 (file)
@@ -83,12 +83,9 @@ test_expect_success "proc-receive: report status v1" '
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/for/main/topic1
        <COMMIT-A> refs/heads/foo
        <COMMIT-B> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 47b058af7ede47e7c8c3bdc2a5238c24e9edbec4..e1e0175c125d034850fd42e293a04d5e2a46bd4b 100644 (file)
@@ -19,13 +19,11 @@ test_expect_success "git-push ($PROTOCOL)" '
         * [new branch] HEAD -> next
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -35,24 +33,22 @@ test_expect_success "git-push --atomic ($PROTOCOL)" '
        test_must_fail git -C workbench push --atomic origin \
                main \
                $B:refs/heads/next \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; }" \
                -e "/^ ! / { p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [rejected] main -> main (non-fast-forward)
         ! [rejected] <COMMIT-B> -> next (atomic push failed)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -65,8 +61,8 @@ test_expect_success "non-fast-forward git-push ($PROTOCOL)" '
                push origin \
                main \
                $B:refs/heads/next \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
@@ -77,13 +73,11 @@ test_expect_success "non-fast-forward git-push ($PROTOCOL)" '
         ! [rejected] main -> main (non-fast-forward)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        <COMMIT-B> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(B)
@@ -119,15 +113,13 @@ test_expect_success "git-push -f ($PROTOCOL)" '
         * [new branch] HEAD -> a/b/c
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/a/b/c
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/review/main/topic
        <TAG-v123> refs/tags/v123
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)  tags/v123  refs/review/main/topic(A)  a/b/c(A)
index bbead12bbb48f5701d041e24954113795e2b90a2..bcbda72341d972193e9cf87c0e23755d7e53fc88 100644 (file)
@@ -20,13 +20,11 @@ test_expect_success "git-push ($PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -36,25 +34,23 @@ test_expect_success "git-push --atomic ($PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --atomic --porcelain origin \
                main \
                $B:refs/heads/next \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "s/^# GETTEXT POISON #//" \
                -e "/^To / { p; }" \
                -e "/^! / { p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
        !    refs/heads/main:refs/heads/main    [rejected] (non-fast-forward)
        !    <COMMIT-B>:refs/heads/next    [rejected] (atomic push failed)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(A)
@@ -67,8 +63,8 @@ test_expect_success "non-fast-forward git-push ($PROTOCOL/porcelain)" '
                push --porcelain origin \
                main \
                $B:refs/heads/next \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/next
@@ -80,13 +76,11 @@ test_expect_success "non-fast-forward git-push ($PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        <COMMIT-B> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)  next(B)
@@ -123,15 +117,13 @@ test_expect_success "git-push -f ($PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/a/b/c
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/review/main/topic
        <TAG-v123> refs/tags/v123
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)  tags/v123  refs/review/main/topic(A)  a/b/c(A)
index e7d113a1587bbb2734e0850ba2341eb452eac090..0c3490c9b1b4483f68a9d1ea26455a77a934e69c 100644 (file)
@@ -12,20 +12,18 @@ test_expect_success "git-push is declined ($PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                $B:refs/heads/main \
                HEAD:refs/heads/next \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [remote rejected] <COMMIT-B> -> main (pre-receive hook declined)
         ! [remote rejected] HEAD -> next (pre-receive hook declined)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "cleanup ($PROTOCOL)" '
index cc0cca6a4721d6a96669b629c713208434b6a26f..e9c9db5d1fc585445cdb50c352465ed51faa9072 100644 (file)
@@ -12,8 +12,8 @@ test_expect_success "git-push is declined ($PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                $B:refs/heads/main \
                HEAD:refs/heads/next \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
        !    <COMMIT-B>:refs/heads/main    [remote rejected] (pre-receive hook declined)
@@ -21,12 +21,10 @@ test_expect_success "git-push is declined ($PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "cleanup ($PROTOCOL/porcelain)" '
index c50830982f73564a727125f85de512cfc24e3e73..3ef136e6ef8eb0ad1e328afc2799c5e7de35ee05 100644 (file)
@@ -5,8 +5,8 @@ test_expect_success "proc-receive: no hook, fail to push special ref ($PROTOCOL)
        test_must_fail git -C workbench push origin \
                HEAD:next \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -19,13 +19,11 @@ test_expect_success "proc-receive: no hook, fail to push special ref ($PROTOCOL)
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -41,8 +39,8 @@ test_expect_success "proc-receive: no hook, all failed for atomic push ($PROTOCO
        test_must_fail git -C workbench push --atomic origin \
                $B:main \
                HEAD:next \
-               HEAD:refs/for/main/topic >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -55,10 +53,8 @@ test_expect_success "proc-receive: no hook, all failed for atomic push ($PROTOCO
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 14ea43348110100fa896009a7781d32585f51568..19f66fbd7dd79480c712f15713678db2ceb32a5a 100644 (file)
@@ -5,8 +5,8 @@ test_expect_success "proc-receive: no hook, fail to push special ref ($PROTOCOL/
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:next \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -20,13 +20,11 @@ test_expect_success "proc-receive: no hook, fail to push special ref ($PROTOCOL/
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -42,8 +40,8 @@ test_expect_success "proc-receive: no hook, all failed for atomic push ($PROTOCO
        test_must_fail git -C workbench push --porcelain --atomic origin \
                $B:main \
                HEAD:next \
-               HEAD:refs/for/main/topic >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -57,10 +55,8 @@ test_expect_success "proc-receive: no hook, all failed for atomic push ($PROTOCO
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index b9be12be773bb4fa14d226202357afe08818b15b..095e613f6fe5c6587490fa34ffd2f30c0c565fef 100644 (file)
@@ -11,8 +11,8 @@ test_expect_success "setup proc-receive hook (unknown version, $PROTOCOL)" '
 test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
 
        # Check status report for git-push
        sed -n \
@@ -34,12 +34,9 @@ test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL)" '
        EOF
        test_cmp expect actual-error &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL)" '
@@ -55,25 +52,22 @@ test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL
 test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; }" \
                -e "/^ ! / { p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-read-version option" out &&
-       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+       grep "remote: fatal: die with the --die-read-version option" out-$test_count &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL)" '
@@ -89,25 +83,22 @@ test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCO
 test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; }" \
                -e "/^ ! / { p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-write-version option" out &&
-       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+       grep "remote: fatal: die with the --die-write-version option" out-$test_count &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL)" '
@@ -123,24 +114,21 @@ test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCO
 test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; }" \
                -e "/^ ! / { p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-read-commands option" out &&
+       grep "remote: fatal: die with the --die-read-commands option" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL)" '
@@ -158,24 +146,21 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-push-options, $
        test_must_fail git -C workbench push origin \
                -o reviewers=user1,user2 \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; }" \
                -e "/^ ! / { p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-read-push-options option" out &&
+       grep "remote: fatal: die with the --die-read-push-options option" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL)" '
@@ -191,24 +176,21 @@ test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL
 test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; }" \
                -e "/^ ! / { p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
         ! [remote rejected] HEAD -> refs/for/main/topic (fail to run proc-receive hook)
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-write-report option" out &&
+       grep "remote: fatal: die with the --die-write-report option" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (no report, $PROTOCOL)" '
@@ -224,8 +206,8 @@ test_expect_success "setup proc-receive hook (no report, $PROTOCOL)" '
 test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/heads/next \
-               HEAD:refs/for/main/topic >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -240,13 +222,10 @@ test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL)" '
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -270,8 +249,8 @@ test_expect_success "setup proc-receive hook (no ref, $PROTOCOL)" '
 test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic\
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -284,12 +263,9 @@ test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL)" '
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL)" '
@@ -306,8 +282,8 @@ test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL)" '
 test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                        HEAD:refs/for/main/topic \
-                       >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+                       >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -320,10 +296,7 @@ test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL)" '
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index fdb4569109ab0192abedd736a45e262f3b3335cb..a44649789cef2b98fe5ec6aa5083da6697df9723 100644 (file)
@@ -11,8 +11,8 @@ test_expect_success "setup proc-receive hook (unknown version, $PROTOCOL/porcela
 test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
 
        # Check status report for git-push
        sed -n \
@@ -34,12 +34,9 @@ test_expect_success "proc-receive: bad protocol (unknown version, $PROTOCOL/porc
        EOF
        test_cmp expect actual-error &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL/porcelain)" '
@@ -55,25 +52,22 @@ test_expect_success "setup proc-receive hook (hook --die-read-version, $PROTOCOL
 test_expect_success "proc-receive: bad protocol (hook --die-read-version, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; n; p; n; p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
        !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
        Done
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-read-version option" out &&
-       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+       grep "remote: fatal: die with the --die-read-version option" out-$test_count &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCOL/porcelain)" '
@@ -89,25 +83,22 @@ test_expect_success "setup proc-receive hook (hook --die-write-version, $PROTOCO
 test_expect_success "proc-receive: bad protocol (hook --die-write-version, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; n; p; n; p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
        !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
        Done
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-write-version option" out &&
-       grep "remote: error: fail to negotiate version with proc-receive hook" out &&
+       grep "remote: fatal: die with the --die-write-version option" out-$test_count &&
+       grep "remote: error: fail to negotiate version with proc-receive hook" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCOL/porcelain)" '
@@ -123,24 +114,21 @@ test_expect_success "setup proc-receive hook (hook --die-read-commands, $PROTOCO
 test_expect_success "proc-receive: bad protocol (hook --die-read-commands, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; n; p; n; p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
        !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
        Done
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-read-commands option" out &&
+       grep "remote: fatal: die with the --die-read-commands option" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-read-push-options, $PROTOCOL/porcelain)" '
@@ -158,24 +146,21 @@ test_expect_success "proc-receive: bad protocol (hook --die-read-push-options, $
        test_must_fail git -C workbench push --porcelain origin \
                -o reviewers=user1,user2 \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; n; p; n; p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
        !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
        Done
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-read-push-options option" out &&
+       grep "remote: fatal: die with the --die-read-push-options option" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL/porcelain)" '
@@ -191,24 +176,21 @@ test_expect_success "setup proc-receive hook (hook --die-write-report, $PROTOCOL
 test_expect_success "proc-receive: bad protocol (hook --die-write-report, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
+               >out-$test_count 2>&1 &&
        filter_out_user_friendly_and_stable_output \
                -e "/^To / { p; n; p; n; p; }" \
-               <out >actual &&
+               <out-$test_count >actual &&
        cat >expect <<-EOF &&
        To <URL/of/upstream.git>
        !    HEAD:refs/for/main/topic    [remote rejected] (fail to run proc-receive hook)
        Done
        EOF
        test_cmp expect actual &&
-       grep "remote: fatal: die with the --die-write-report option" out &&
+       grep "remote: fatal: die with the --die-write-report option" out-$test_count &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (no report, $PROTOCOL/porcelain)" '
@@ -224,8 +206,8 @@ test_expect_success "setup proc-receive hook (no report, $PROTOCOL/porcelain)" '
 test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/heads/next \
-               HEAD:refs/for/main/topic >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               HEAD:refs/for/main/topic >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/heads/next
@@ -241,13 +223,10 @@ test_expect_success "proc-receive: bad protocol (no report, $PROTOCOL/porcelain)
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
@@ -270,8 +249,8 @@ test_expect_success "setup proc-receive hook (no ref, $PROTOCOL/porcelain)" '
 test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic\
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -285,12 +264,9 @@ test_expect_success "proc-receive: bad protocol (no ref, $PROTOCOL/porcelain)" '
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL/porcelain)" '
@@ -307,8 +283,8 @@ test_expect_success "setup proc-receive hook (unknown status, $PROTOCOL/porcelai
 test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                        HEAD:refs/for/main/topic \
-                       >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+                       >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -322,10 +298,7 @@ test_expect_success "proc-receive: bad protocol (unknown status, $PROTOCOL/porce
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 5a9e0daf2d56829b9372a85b499ff1a815c8be62..ad2c8f65355d94765c40ea7491f2fa3200a526bb 100644 (file)
@@ -12,8 +12,8 @@ test_expect_success "setup proc-receive hook (ng, no message, $PROTOCOL)" '
 test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -24,12 +24,10 @@ test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL)" '
         ! [remote rejected] HEAD -> refs/for/main/topic (failed)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (ng message, $PROTOCOL)" '
@@ -46,8 +44,8 @@ test_expect_success "setup proc-receive hook (ng message, $PROTOCOL)" '
 test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -58,10 +56,8 @@ test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL)"
         ! [remote rejected] HEAD -> refs/for/main/topic (error msg)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 93475a83cfcb9c9e0faeed7d94a4bec6cec3ffaa..d8ae9d3414db6793ee1f20ad6ad05b3dcd3bca0e 100644 (file)
@@ -12,8 +12,8 @@ test_expect_success "setup proc-receive hook (ng, no message, $PROTOCOL/porcelai
 test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -25,12 +25,10 @@ test_expect_success "proc-receive: fail to update (ng, no message, $PROTOCOL/por
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (ng message, $PROTOCOL/porcelain)" '
@@ -47,8 +45,8 @@ test_expect_success "setup proc-receive hook (ng message, $PROTOCOL/porcelain)"
 test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -60,10 +58,8 @@ test_expect_success "proc-receive: fail to update (ng, with message, $PROTOCOL/p
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index f8be8a0ba1adcf08ba8564406999c1068f19a1c5..dbed467186a6cf8fae9b9dc66fe227cd25ff10a9 100644 (file)
@@ -13,8 +13,8 @@ test_expect_success "proc-receive: report unexpected ref ($PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                $B:refs/heads/main \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -30,12 +30,10 @@ test_expect_success "proc-receive: report unexpected ref ($PROTOCOL)" '
         ! [remote rejected] HEAD -> refs/for/main/topic (proc-receive failed to report status)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)
index 778150fa03257ae0e3213a3b86f2ab5da2d82ce4..e89096fa13b65c5df4bbe3358c75ccaf9303adf0 100644 (file)
@@ -13,8 +13,8 @@ test_expect_success "proc-receive: report unexpected ref ($PROTOCOL/porcelain)"
        test_must_fail git -C workbench push --porcelain origin \
                $B:refs/heads/main \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -31,12 +31,10 @@ test_expect_success "proc-receive: report unexpected ref ($PROTOCOL/porcelain)"
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)
index d4e74e46811ee880edf14277b49cb8443e075c9a..77204244b860017722faa0f9cecde432de2d4b69 100644 (file)
@@ -12,8 +12,8 @@ test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL)" '
 test_expect_success "proc-receive: report unknown reference ($PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/a/b/c/my/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
@@ -25,10 +25,8 @@ test_expect_success "proc-receive: report unknown reference ($PROTOCOL)" '
         ! [remote rejected] HEAD -> refs/for/a/b/c/my/topic (proc-receive failed to report status)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 039e8b61637ce585c1f98fb03267e257132ed205..eeb1ce6b2c7db8b96b7970c5135a5dff78a839e1 100644 (file)
@@ -12,8 +12,8 @@ test_expect_success "setup proc-receive hook (unexpected ref, $PROTOCOL/porcelai
 test_expect_success "proc-receive: report unknown reference ($PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/a/b/c/my/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/a/b/c/my/topic
@@ -26,10 +26,8 @@ test_expect_success "proc-receive: report unknown reference ($PROTOCOL/porcelain
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index e88edb16a445c88f008f2b82aab8c61e4ff47d55..1ec2cb95bcd54bc2ba4b513c8082b971b111f729 100644 (file)
@@ -16,16 +16,14 @@ test_expect_success "proc-receive: not support push options ($PROTOCOL)" '
                -o reviewer=user1 \
                origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        test_i18ngrep "fatal: the receiving end does not support push options" \
                actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "enable push options ($PROTOCOL)" '
@@ -69,13 +67,11 @@ test_expect_success "proc-receive: ignore push-options for version 0 ($PROTOCOL)
         * [new reference] HEAD -> refs/for/main/topic
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "restore proc-receive hook ($PROTOCOL)" '
@@ -123,13 +119,11 @@ test_expect_success "proc-receive: push with options ($PROTOCOL)" '
         * [new reference] HEAD -> refs/for/main/topic
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
index 3a6561b5eab465b3b9eab20e29a3c4fa992d6a7c..447fbfec0c9045da74479d08f563ce1d175077be 100644 (file)
@@ -17,16 +17,14 @@ test_expect_success "proc-receive: not support push options ($PROTOCOL/porcelain
                -o reviewer=user1 \
                origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        test_i18ngrep "fatal: the receiving end does not support push options" \
                actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "enable push options ($PROTOCOL/porcelain)" '
@@ -72,13 +70,11 @@ test_expect_success "proc-receive: ignore push-options for version 0 ($PROTOCOL/
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "restore proc-receive hook ($PROTOCOL/porcelain)" '
@@ -128,13 +124,11 @@ test_expect_success "proc-receive: push with options ($PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/next
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             next(A)
index 5d6feef118f7e4f4d25b7d9474528fe70b9f987d..8acb4f204fb3c44f9cad1921b5fadddddd0ceca7 100644 (file)
@@ -26,10 +26,8 @@ test_expect_success "proc-receive: ok ($PROTOCOL)" '
         * [new reference] HEAD -> refs/for/main/topic
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 91666d32df5fccd76c80cae4592ef6efb2532773..a967718046491f34b7afe4d85a1e23657da44b2b 100644 (file)
@@ -27,10 +27,8 @@ test_expect_success "proc-receive: ok ($PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index a0faf5c7ff04d414f22e79e688bcc23a0452fff0..437ade012d4007b454e6318683c4346e7ff49d48 100644 (file)
@@ -13,8 +13,8 @@ test_expect_success "setup proc-receive hook (option without matching ok, $PROTO
 test_expect_success "proc-receive: report option without matching ok ($PROTOCOL)" '
        test_must_fail git -C workbench push origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -247,10 +247,7 @@ test_expect_success "proc-receive: report with multiple rewrites ($PROTOCOL)" '
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 32ae26bcfb0e5b9b2ce106fe1443ecaed9fd0cb9..11486720eeffd860e1bb708c4715112e0c52ee72 100644 (file)
@@ -13,8 +13,8 @@ test_expect_success "setup proc-receive hook (option without matching ok, $PROTO
 test_expect_success "proc-receive: report option without matching ok ($PROTOCOL/porcelain)" '
        test_must_fail git -C workbench push --porcelain origin \
                HEAD:refs/for/main/topic \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <ZERO-OID> <COMMIT-A> refs/for/main/topic
@@ -256,10 +256,7 @@ test_expect_success "proc-receive: report with multiple rewrites ($PROTOCOL/porc
        EOF
        test_cmp expect actual &&
 
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index c355c290d2f0c56c7e7408902f5434f6908644e0..6e0d08b3271a8d417a4b5e86682515d63b2fcf53 100644 (file)
@@ -28,13 +28,11 @@ test_expect_success "proc-receive: fall throught, let receive-pack to execute ($
         * [new reference] <COMMIT-B> -> refs/for/main/topic
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/for/main/topic
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             refs/for/main/topic(A)
index 8ce4e58f2a5c483137946766a2d3ad8a7adab280..81bae9f2ec25530f3ce7cd5cfd9be4ea4be8722e 100644 (file)
@@ -29,13 +29,11 @@ test_expect_success "proc-receive: fall throught, let receive-pack to execute ($
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/for/main/topic
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             refs/for/main/topic(A)
index fad8eea8a060f640e76e6b93b076a95c95660982..be9b18b2b69e22ff6dc490a847b0b25c25d099a5 100644 (file)
@@ -65,12 +65,10 @@ test_expect_success "proc-receive: multiple rewrite for one ref, no refname for
         <OID-A>..<OID-B> HEAD -> refs/changes/25/125/1
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "proc-receive: check remote-tracking #1 ($PROTOCOL)" '
@@ -142,12 +140,10 @@ test_expect_success "proc-receive: multiple rewrites for one ref, no refname for
         + <OID-B>...<OID-A> HEAD -> refs/changes/25/125/1 (forced update)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "proc-receive: check remote-tracking #2 ($PROTOCOL)" '
@@ -205,12 +201,10 @@ test_expect_success "proc-receive: multiple rewrites for one ref ($PROTOCOL)" '
         <OID-A>..<OID-B> HEAD -> refs/changes/24/124/2
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "proc-receive: check remote-tracking #3 ($PROTOCOL)" '
index dc254d57eb0ddbe8aa6e2803bf86c58980600d70..95fb89c031d1625f8a99712d8089c1e8d54c715d 100644 (file)
@@ -51,12 +51,10 @@ test_expect_success "proc-receive: multiple rewrite for one ref, no refname for
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (multiple rewrites for one ref, no refname for the 2nd rewrite, $PROTOCOL/porcelain)" '
@@ -114,12 +112,10 @@ test_expect_success "proc-receive: multiple rewrites for one ref, no refname for
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook (multiple rewrites for one ref, $PROTOCOL/porcelain)" '
@@ -163,10 +159,8 @@ test_expect_success "proc-receive: multiple rewrites for one ref ($PROTOCOL/porc
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
index 0d071ebaa6c9dc8cd40bca37d8e4806d2b94cd6e..5e005299cc04af853d6bcbe906af09825e75d4ec 100644 (file)
@@ -24,8 +24,8 @@ test_expect_success "proc-receive: report update of mixed refs ($PROTOCOL)" '
                HEAD:refs/heads/foo \
                HEAD:refs/for/main/topic \
                HEAD:refs/for/next/topic3 \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -65,15 +65,13 @@ test_expect_success "proc-receive: report update of mixed refs ($PROTOCOL)" '
         ! [remote rejected] HEAD -> refs/for/next/topic3 (proc-receive failed to report status)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/bar
        <COMMIT-A> refs/heads/baz
        <COMMIT-A> refs/heads/foo
        <COMMIT-B> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)             foo(A)  bar(A))  baz(A)
index d8409912fd3af493f116a0ae735f0b5bd821552a..8f891c5385cf3f3e33988e8857ea37fa4f318dc1 100644 (file)
@@ -24,8 +24,8 @@ test_expect_success "proc-receive: report update of mixed refs ($PROTOCOL/porcel
                HEAD:refs/heads/foo \
                HEAD:refs/for/main/topic \
                HEAD:refs/for/next/topic3 \
-               >out 2>&1 &&
-       make_user_friendly_and_stable_output <out >actual &&
+               >out-$test_count 2>&1 &&
+       make_user_friendly_and_stable_output <out-$test_count >actual &&
        cat >expect <<-EOF &&
        remote: # pre-receive hook
        remote: pre-receive< <COMMIT-A> <COMMIT-B> refs/heads/main
@@ -66,15 +66,13 @@ test_expect_success "proc-receive: report update of mixed refs ($PROTOCOL/porcel
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/bar
        <COMMIT-A> refs/heads/baz
        <COMMIT-A> refs/heads/foo
        <COMMIT-B> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(B)             foo(A)  bar(A))  baz(A)
index 2565302a1791efc131597c2160c3e574d6ea92a4..fdcdcc7c2eeae4008b9d5dcd548b951914f4a6c6 100644 (file)
@@ -92,14 +92,12 @@ test_expect_success "proc-receive: process all refs ($PROTOCOL)" '
         + <OID-B>...<OID-A> HEAD -> refs/pull/124/head (forced update)
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/bar
        <COMMIT-A> refs/heads/baz
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             bar(A)  baz(B)
index e21420b60dd823a589c06e1885ea2276fbed573f..73b35fe0aa5dbdc42b1ddb479c1eb3614c2d496b 100644 (file)
@@ -93,14 +93,12 @@ test_expect_success "proc-receive: process all refs ($PROTOCOL/porcelain)" '
        Done
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-B> refs/heads/bar
        <COMMIT-A> refs/heads/baz
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)             bar(A)  baz(B)
index 2e29518ec5fb737ac40940f52a3b50cc99d9ba17..7214647ada6c5cdff18a10b6d0031c08444e613b 100644 (file)
@@ -50,12 +50,10 @@ test_expect_success "proc-receive: update branch and new tag ($PROTOCOL)" '
         * [new reference] v123 -> refs/pull/124/head
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        EOF
-       test_cmp expect actual
 '
 
 # Refs of upstream : main(A)
@@ -63,14 +61,12 @@ test_expect_success "proc-receive: update branch and new tag ($PROTOCOL)" '
 test_expect_success "setup upstream: create tags/v123 ($PROTOCOL)" '
        git -C "$upstream" update-ref refs/heads/topic $A &&
        git -C "$upstream" update-ref refs/tags/v123 $TAG &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-A> refs/heads/topic
        <TAG-v123> refs/tags/v123
        EOF
-       test_cmp expect actual
 '
 
 test_expect_success "setup proc-receive hook ($PROTOCOL)" '
@@ -125,11 +121,9 @@ test_expect_success "proc-receive: create/delete branch, and delete tag ($PROTOC
         * [new reference] <COMMIT-A> -> refs/pull/124/head
        EOF
        test_cmp expect actual &&
-       git -C "$upstream" show-ref >out &&
-       make_user_friendly_and_stable_output <out >actual &&
-       cat >expect <<-EOF &&
+
+       test_cmp_refs -C "$upstream" <<-EOF
        <COMMIT-A> refs/heads/main
        <COMMIT-B> refs/heads/topic
        EOF
-       test_cmp expect actual
 '
index 1f4cc873a837ee305396c9975a4cd08919a028ab..8a5d3492c713b8ca5eceef13ed55f7bd49edcae9 100755 (executable)
@@ -547,7 +547,7 @@ test_expect_success 'test lonely missing ref' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy 2>../error-m
        ) &&
-       test_i18ncmp expect-error error-m
+       test_cmp expect-error error-m
 '
 
 test_expect_success 'test missing ref after existing' '
@@ -555,7 +555,7 @@ test_expect_success 'test missing ref after existing' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy 2>../error-em
        ) &&
-       test_i18ncmp expect-error error-em
+       test_cmp expect-error error-em
 '
 
 test_expect_success 'test missing ref before existing' '
@@ -563,7 +563,7 @@ test_expect_success 'test missing ref before existing' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A 2>../error-me
        ) &&
-       test_i18ncmp expect-error error-me
+       test_cmp expect-error error-me
 '
 
 test_expect_success 'test --all, --depth, and explicit head' '
index 045398b94e63f88a09c6354a0e37ec8f85fab284..8c462f20ae63e73a1191bc3bae0160eed2d267ae 100755 (executable)
@@ -59,7 +59,7 @@ test_expect_success 'add remote whose URL agrees with url.<...>.insteadOf' '
        git remote add myremote git@host.com:team/repo.git
 '
 
-test_expect_success C_LOCALE_OUTPUT 'remote information for the origin' '
+test_expect_success 'remote information for the origin' '
        (
                cd test &&
                tokens_match origin "$(git remote)" &&
@@ -81,7 +81,7 @@ test_expect_success 'add another remote' '
        )
 '
 
-test_expect_success C_LOCALE_OUTPUT 'check remote-tracking' '
+test_expect_success 'check remote-tracking' '
        (
                cd test &&
                check_remote_track origin main side &&
@@ -107,7 +107,7 @@ test_expect_success 'remove remote' '
        )
 '
 
-test_expect_success C_LOCALE_OUTPUT 'remove remote' '
+test_expect_success 'remove remote' '
        (
                cd test &&
                tokens_match origin "$(git remote)" &&
@@ -140,8 +140,8 @@ test_expect_success 'remove remote protects local branches' '
                git remote rm oops 2>actual2 &&
                git branch -d foobranch &&
                git tag -d footag &&
-               test_i18ncmp expect1 actual1 &&
-               test_i18ncmp expect2 actual2
+               test_cmp expect1 actual1 &&
+               test_cmp expect2 actual2
        )
 '
 
@@ -150,7 +150,7 @@ test_expect_success 'remove errors out early when deleting non-existent branch'
                cd test &&
                echo "error: No such remote: '\''foo'\''" >expect &&
                test_expect_code 2 git remote rm foo 2>actual &&
-               test_i18ncmp expect actual
+               test_cmp expect actual
        )
 '
 
@@ -178,7 +178,7 @@ test_expect_success 'rename errors out early when deleting non-existent branch'
                cd test &&
                echo "error: No such remote: '\''foo'\''" >expect &&
                test_expect_code 2 git remote rename foo bar 2>actual &&
-               test_i18ncmp expect actual
+               test_cmp expect actual
        )
 '
 
@@ -186,14 +186,14 @@ test_expect_success 'rename errors out early when when new name is invalid' '
        test_config remote.foo.vcs bar &&
        echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
        test_must_fail git remote rename foo invalid...name 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'add existing foreign_vcs remote' '
        test_config remote.foo.vcs bar &&
        echo "error: remote foo already exists." >expect &&
        test_expect_code 3 git remote add foo bar 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'add existing foreign_vcs remote' '
@@ -201,13 +201,13 @@ test_expect_success 'add existing foreign_vcs remote' '
        test_config remote.bar.vcs bar &&
        echo "error: remote bar already exists." >expect &&
        test_expect_code 3 git remote rename foo bar 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'add invalid foreign_vcs remote' '
        echo "fatal: '\''invalid...name'\'' is not a valid remote name" >expect &&
        test_must_fail git remote add invalid...name bar 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >test/expect <<EOF
@@ -267,7 +267,7 @@ test_expect_success 'show' '
                git config --add remote.two.push refs/heads/main:refs/heads/another &&
                git remote show origin two >output &&
                git branch -d rebase octopus &&
-               test_i18ncmp expect output
+               test_cmp expect output
        )
 '
 
@@ -294,7 +294,7 @@ test_expect_success 'show -n' '
                cd test &&
                git remote show -n origin >output &&
                mv ../one.unreachable ../one &&
-               test_i18ncmp expect output
+               test_cmp expect output
        )
 '
 
@@ -337,7 +337,7 @@ test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
                git fetch two "refs/heads/*:refs/remotes/two/*" &&
                git remote set-head --auto two >output 2>&1 &&
                echo "two/HEAD set to main" >expect &&
-               test_i18ncmp expect output
+               test_cmp expect output
        )
 '
 
@@ -369,7 +369,7 @@ test_expect_success 'prune --dry-run' '
                git remote prune --dry-run origin >output &&
                git rev-parse refs/remotes/origin/side2 &&
                test_must_fail git rev-parse refs/remotes/origin/side &&
-               test_i18ncmp expect output
+               test_cmp expect output
        )
 '
 
index 42f550300476a1e7ddae6b88dc0a04bb89ff0d80..e83b2a65069f6a1ddf78da5a38efd3f5f85c65e8 100755 (executable)
@@ -9,7 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/test-bundle-functions.sh
+. "$TEST_DIRECTORY"/lib-bundle.sh
 
 D=$(pwd)
 
@@ -1051,7 +1051,7 @@ test_expect_success 'fetch --prune prints the remotes url' '
                git fetch --prune origin 2>&1 | head -n1 >../actual
        ) &&
        echo "From ${D}/." >expect &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'branchname D/F conflict resolved by --prune' '
@@ -1097,7 +1097,7 @@ test_expect_success 'fetching with auto-gc does not lock up' '
        )
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
+test_expect_success 'fetch aligned output' '
        git clone . full-output &&
        test_commit looooooooooooong-tag &&
        (
@@ -1112,7 +1112,7 @@ test_expect_success C_LOCALE_OUTPUT 'fetch aligned output' '
        test_cmp expect actual
 '
 
-test_expect_success C_LOCALE_OUTPUT 'fetch compact output' '
+test_expect_success 'fetch compact output' '
        git clone . compact &&
        test_commit extraaa &&
        (
index 4a568a2398ef4ba96ca2218839f9c2f2741b833d..f53f58895a12979f27a1b24ba9f15ee37bbc49a9 100755 (executable)
@@ -150,7 +150,7 @@ test_expect_success 'confuses pattern as remote when no remote specified' '
        # We could just as easily have used "main"; the "*" emphasizes its
        # role as a pattern.
        test_must_fail git ls-remote "$does_not_exist" >actual 2>&1 &&
-       test_i18ncmp exp actual
+       test_cmp exp actual
 '
 
 test_expect_success 'die with non-2 for wrong repository even with --exit-code' '
index 11513ec15e1e90cc2ee15a3f501d0789f393fe0f..ed11569d8d7ec605198773351e75baeaa232cb1a 100755 (executable)
@@ -74,7 +74,7 @@ test_expect_success "fetch --recurse-submodules recurses into submodules" '
                git fetch --recurse-submodules >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "submodule.recurse option triggers recursive fetch" '
@@ -84,7 +84,7 @@ test_expect_success "submodule.recurse option triggers recursive fetch" '
                git -c submodule.recurse fetch >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
@@ -94,7 +94,7 @@ test_expect_success "fetch --recurse-submodules -j2 has the same output behaviou
                GIT_TRACE="$TRASH_DIRECTORY/trace.out" git fetch --recurse-submodules -j2 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err &&
+       test_cmp expect.err actual.err &&
        grep "2 tasks" trace.out
 '
 
@@ -124,7 +124,7 @@ test_expect_success "using fetchRecurseSubmodules=true in .gitmodules recurses i
                git fetch >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "--no-recurse-submodules overrides .gitmodules config" '
@@ -155,7 +155,7 @@ test_expect_success "--recurse-submodules overrides fetchRecurseSubmodules setti
                git config --unset submodule.submodule.fetchRecurseSubmodules
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "--quiet propagates to submodules" '
@@ -183,7 +183,7 @@ test_expect_success "--dry-run propagates to submodules" '
                git fetch --recurse-submodules --dry-run >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "Without --dry-run propagates to submodules" '
@@ -192,7 +192,7 @@ test_expect_success "Without --dry-run propagates to submodules" '
                git fetch --recurse-submodules >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "recurseSubmodules=true propagates into submodules" '
@@ -203,7 +203,7 @@ test_expect_success "recurseSubmodules=true propagates into submodules" '
                git fetch >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "--recurse-submodules overrides config in submodule" '
@@ -217,7 +217,7 @@ test_expect_success "--recurse-submodules overrides config in submodule" '
                git fetch --recurse-submodules >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "--no-recurse-submodules overrides config setting" '
@@ -257,7 +257,7 @@ test_expect_success "Recursion stops when no new submodule commits are fetched"
                cd downstream &&
                git fetch >../actual.out 2>../actual.err
        ) &&
-       test_i18ncmp expect.err.sub actual.err &&
+       test_cmp expect.err.sub actual.err &&
        test_must_be_empty actual.out
 '
 
@@ -275,7 +275,7 @@ test_expect_success "Recursion doesn't happen when new superproject commits don'
                git fetch >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err.file actual.err
+       test_cmp expect.err.file actual.err
 '
 
 test_expect_success "Recursion picks up config in submodule" '
@@ -303,7 +303,7 @@ test_expect_success "Recursion picks up config in submodule" '
                        git config --unset fetch.recurseSubmodules
                )
        ) &&
-       test_i18ncmp expect.err.sub actual.err &&
+       test_cmp expect.err.sub actual.err &&
        test_must_be_empty actual.out
 '
 
@@ -336,7 +336,7 @@ test_expect_success "Recursion picks up all submodules when necessary" '
                cd downstream &&
                git fetch >../actual.out 2>../actual.err
        ) &&
-       test_i18ncmp expect.err.2 actual.err &&
+       test_cmp expect.err.2 actual.err &&
        test_must_be_empty actual.out
 '
 
@@ -392,7 +392,7 @@ test_expect_success "'--recurse-submodules=on-demand' recurses as deep as necess
                )
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err
+       test_cmp expect.err actual.err
 '
 
 test_expect_success "'--recurse-submodules=on-demand' stops when no new submodule commits are found in the superproject (and ignores config)" '
@@ -409,7 +409,7 @@ test_expect_success "'--recurse-submodules=on-demand' stops when no new submodul
                git fetch --recurse-submodules=on-demand >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err.file actual.err
+       test_cmp expect.err.file actual.err
 '
 
 test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config" '
@@ -437,7 +437,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' overrides global config
                git config --unset fetch.recurseSubmodules
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err.2 actual.err
+       test_cmp expect.err.2 actual.err
 '
 
 test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' overrides fetch.recurseSubmodules" '
@@ -465,7 +465,7 @@ test_expect_success "'submodule.<sub>.fetchRecurseSubmodules=on-demand' override
                git config --unset submodule.submodule.fetchRecurseSubmodules
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err.2 actual.err
+       test_cmp expect.err.2 actual.err
 '
 
 test_expect_success "don't fetch submodule when newly recorded commits are already present" '
@@ -484,7 +484,7 @@ test_expect_success "don't fetch submodule when newly recorded commits are alrea
                git fetch >../actual.out 2>../actual.err
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err actual.err &&
+       test_cmp expect.err actual.err &&
        (
                cd submodule &&
                git checkout -q sub
@@ -520,7 +520,7 @@ test_expect_success "'fetch.recurseSubmodules=on-demand' works also without .git
                git reset --hard
        ) &&
        test_must_be_empty actual.out &&
-       test_i18ncmp expect.err.2 actual.err &&
+       test_cmp expect.err.2 actual.err &&
        git checkout HEAD^ -- .gitmodules &&
        git add .gitmodules &&
        git commit -m "new submodule restored .gitmodules"
index bc5ccf233f02f8cf641abee4c3007148e4da365b..c024fa2818314d3d127e93da2e281dc259820733 100755 (executable)
@@ -118,7 +118,7 @@ test_expect_success 'rejected update prints status' '
        git commit -m dev2 &&
        test_must_fail git push origin dev2 2>act &&
        sed -e "/^remote: /s/ *$//" <act >cmp &&
-       test_i18ncmp exp cmp
+       test_cmp exp cmp
 '
 rm -f "$HTTPD_DOCUMENT_ROOT_PATH/test_repo.git/hooks/update"
 
index 4357af15256647588b4c7d8a515eddd45835e7c7..dd5f44d986f2f774313ca11e7a1bfecf87060e7e 100755 (executable)
@@ -59,4 +59,14 @@ test_expect_success 'hook does not run from repo config' '
        test_path_is_missing .git/hook.stdout
 '
 
+test_expect_success 'hook works with partial clone' '
+       clear_hook_results &&
+       test_config_global uploadpack.packObjectsHook ./hook &&
+       test_config_global uploadpack.allowFilter true &&
+       git clone --bare --no-local --filter=blob:none . dst.git &&
+       git -C dst.git rev-list --objects --missing=allow-any --no-object-names --all >objects &&
+       git -C dst.git cat-file --batch-check="%(objecttype)" <objects >types &&
+       ! grep blob types
+'
+
 test_done
index 664c913866236dcbb026a1222f18b1b5d41cf163..e7e6c089554c5d2b0ba7cd51b79a53f6880f0ae1 100755 (executable)
@@ -40,7 +40,7 @@ test_expect_success 'clone with excess parameters (2)' '
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'output from clone' '
+test_expect_success 'output from clone' '
        rm -fr dst &&
        git clone -n "file://$(pwd)/src" dst >output 2>&1 &&
        test $(grep Clon output | wc -l) = 1
index 5d682706ae4a7f726a639d11fed826c842d48908..e845d621f618ba83962842b766c076a788877936 100755 (executable)
@@ -329,7 +329,7 @@ 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-1 &&
+                   -e "/multi-pack-index/d" -e "/rev/d" <$raw >$raw.de-sha-1 &&
                sort $raw.de-sha-1 >$raw.de-sha || return 1
        done &&
 
index 5d6e63a841f7f2f64a0fa857261264c4bd924c67..52e5789fb050d74db72d1ad62dab1cce5ba97793 100755 (executable)
@@ -105,11 +105,13 @@ test_expect_success 'redirected clone -v does show progress' '
 '
 
 test_expect_success 'chooses correct default initial branch name' '
-       git init --bare empty &&
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+       git -c init.defaultBranch=foo init --bare empty &&
+       test_config -C empty lsrefs.unborn advertise &&
        GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
        git -c init.defaultBranch=up clone empty whats-up &&
-       test refs/heads/up = $(git -C whats-up symbolic-ref HEAD) &&
-       test refs/heads/up = $(git -C whats-up config branch.up.merge)
+       test refs/heads/foo = $(git -C whats-up symbolic-ref HEAD) &&
+       test refs/heads/foo = $(git -C whats-up config branch.foo.merge)
 '
 
 test_expect_success 'guesses initial branch name correctly' '
index d9143b4bd2d7ca27dd689c231774b8d143baa798..509f379d4920605e567646692c15146fd977b2cd 100755 (executable)
@@ -15,7 +15,7 @@ test_expect_success 'test capability advertisement' '
        cat >expect <<-EOF &&
        version 2
        agent=git/$(git version | cut -d" " -f3)
-       ls-refs
+       ls-refs=unborn
        fetch=shallow
        server-option
        object-format=$(test_oid algo)
index 3d994e0b1bb2a9e75488e2fdeec6ae8c5111bef2..9113d209c5145b93c1304d17715568afe1fd0fa6 100755 (executable)
@@ -212,6 +212,31 @@ test_expect_success 'clone with file:// using protocol v2' '
        grep "ref-prefix refs/tags/" log
 '
 
+test_expect_success 'clone of empty repo propagates name of default branch' '
+       test_when_finished "rm -rf file_empty_parent file_empty_child" &&
+
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+       git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
+
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+       git -c init.defaultBranch=main -c protocol.version=2 \
+               clone "file://$(pwd)/file_empty_parent" file_empty_child &&
+       grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+'
+
+test_expect_success '...but not if explicitly forbidden by config' '
+       test_when_finished "rm -rf file_empty_parent file_empty_child" &&
+
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+       git -c init.defaultBranch=mydefaultbranch init file_empty_parent &&
+       test_config -C file_empty_parent lsrefs.unborn ignore &&
+
+       GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME= \
+       git -c init.defaultBranch=main -c protocol.version=2 \
+               clone "file://$(pwd)/file_empty_parent" file_empty_child &&
+       ! grep "refs/heads/mydefaultbranch" file_empty_child/.git/HEAD
+'
+
 test_expect_success 'fetch with file:// using protocol v2' '
        test_when_finished "rm -f log" &&
 
@@ -851,8 +876,10 @@ test_expect_success 'part of packfile response provided as URI' '
        test -f h2found &&
 
        # Ensure that there are exactly 6 files (3 .pack and 3 .idx).
-       ls http_child/.git/objects/pack/* >filelist &&
-       test_line_count = 6 filelist
+       ls http_child/.git/objects/pack/*.pack >packlist &&
+       ls http_child/.git/objects/pack/*.idx >idxlist &&
+       test_line_count = 3 idxlist &&
+       test_line_count = 3 packlist
 '
 
 test_expect_success 'fetching with valid packfile URI but invalid hash fails' '
@@ -905,8 +932,10 @@ test_expect_success 'packfile-uri with transfer.fsckobjects' '
                clone "$HTTPD_URL/smart/http_parent" http_child &&
 
        # Ensure that there are exactly 4 files (2 .pack and 2 .idx).
-       ls http_child/.git/objects/pack/* >filelist &&
-       test_line_count = 4 filelist
+       ls http_child/.git/objects/pack/*.pack >packlist &&
+       ls http_child/.git/objects/pack/*.idx >idxlist &&
+       test_line_count = 2 idxlist &&
+       test_line_count = 2 packlist
 '
 
 test_expect_success 'packfile-uri with transfer.fsckobjects fails on bad object' '
index 64d8f99325165a76008a478a55cfa913957293a2..e9e471621d59a6a321699c1c59138b95c05b2525 100755 (executable)
@@ -19,7 +19,8 @@ get_actual_commits () {
        test-tool pkt-line unpack-sideband <out >o.pack &&
        git index-pack o.pack &&
        git verify-pack -v o.idx >objs &&
-       grep commit objs | cut -d" " -f1 | sort >actual_commits
+       sed -n -e 's/\([0-9a-f][0-9a-f]*\) commit .*/\1/p' objs >objs.sed &&
+       sort >actual_commits <objs.sed
 }
 
 check_output () {
index 6249420a80672b98b85b93cefaa65b4d7f8c7151..881f72fd443c8aefbe47c693b5bb80ee842a64db 100755 (executable)
@@ -9,7 +9,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/test-bundle-functions.sh
+. "$TEST_DIRECTORY"/lib-bundle.sh
 
 # Create a commit or tag and set the variable with the object ID.
 test_commit_setvar () {
@@ -175,7 +175,7 @@ test_expect_success 'create bundle from special rev: main^!' '
        cat >expect <<-\EOF &&
        <COMMIT-P> refs/heads/main
        EOF
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        git bundle verify special-rev.bdl |
                make_user_friendly_and_stable_output >actual &&
@@ -185,7 +185,7 @@ test_expect_success 'create bundle from special rev: main^!' '
        The bundle requires this ref:
        <COMMIT-O>
        EOF
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_bundle_object_count special-rev.bdl 3
 '
@@ -207,7 +207,7 @@ test_expect_success 'create bundle with --max-count option' '
        The bundle requires this ref:
        <COMMIT-O>
        EOF
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_bundle_object_count max-count.bdl 4
 '
@@ -236,7 +236,7 @@ test_expect_success 'create bundle with --since option' '
        <COMMIT-M>
        <COMMIT-K>
        EOF
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_bundle_object_count --thin since.bdl 13
 '
@@ -262,11 +262,11 @@ test_expect_success 'create bundle 1 - no prerequisites' '
        # verify bundle, which has no prerequisites
        git bundle verify 1.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        git bundle verify stdin-1.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_bundle_object_count       1.bdl 24 &&
        test_bundle_object_count stdin-1.bdl 24
@@ -304,11 +304,11 @@ test_expect_success 'create bundle 2 - has prerequisites' '
 
        git bundle verify 2.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        git bundle verify stdin-2.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_bundle_object_count       2.bdl 16 &&
        test_bundle_object_count stdin-2.bdl 16
@@ -326,11 +326,11 @@ test_expect_success 'fail to verify bundle without prerequisites' '
 
        test_must_fail git -C test1.git bundle verify ../2.bdl 2>&1 |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_must_fail git -C test1.git bundle verify ../stdin-2.bdl 2>&1 |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'create bundle 3 - two refs, same object' '
@@ -363,11 +363,11 @@ test_expect_success 'create bundle 3 - two refs, same object' '
 
        git bundle verify 3.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        git bundle verify stdin-3.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_bundle_object_count       3.bdl 4 &&
        test_bundle_object_count stdin-3.bdl 4
@@ -404,11 +404,11 @@ test_expect_success 'create bundle 4 - with tags' '
 
        git bundle verify 4.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        git bundle verify stdin-4.bdl |
                make_user_friendly_and_stable_output >actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
 
        test_bundle_object_count       4.bdl 3 &&
        test_bundle_object_count stdin-4.bdl 3
index 7bcde054d73d5815c3bdc52c5c53ebd3e3eb32d4..ef7bdcedf2304ef3e6d1379f29742dcfcf5ee28b 100755 (executable)
@@ -829,7 +829,7 @@ test_expect_success 'bisect terms needs 0 or 1 argument' '
        test_must_fail git bisect terms 1 2 &&
        test_must_fail git bisect terms 2>actual &&
        echo "error: no terms defined" >expected &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'bisect terms shows good/bad after start' '
@@ -903,7 +903,7 @@ test_expect_success 'bisect start --term-* does store terms' '
        Your current terms are two for the old state
        and one for the new state.
        EOF
-       test_i18ncmp expected actual &&
+       test_cmp expected actual &&
        git bisect terms --term-bad >actual &&
        echo one >expected &&
        test_cmp expected actual &&
index a160b2bf99c41c3b30dcd26ebae0a78f6a96b3f8..a313849406ddb4b7f104ee06eabc9068d5551425 100755 (executable)
@@ -57,7 +57,7 @@ test_expect_success 'branch -v' '
                git branch -v
        ) |
        sed -n -e "$t6040_script" >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -75,7 +75,7 @@ test_expect_success 'branch -vv' '
                git branch -vv
        ) |
        sed -n -e "$t6040_script" >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'checkout (diverged from upstream)' '
@@ -146,7 +146,7 @@ test_expect_success 'status -s -b (diverged from upstream)' '
                git checkout b1 >/dev/null &&
                git status -s -b | head -1
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -159,7 +159,7 @@ test_expect_success 'status -s -b --no-ahead-behind (diverged from upstream)' '
                git checkout b1 >/dev/null &&
                git status -s -b --no-ahead-behind | head -1
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -172,7 +172,7 @@ test_expect_success 'status.aheadbehind=false status -s -b (diverged from upstre
                git checkout b1 >/dev/null &&
                git -c status.aheadbehind=false status -s -b | head -1
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -187,7 +187,7 @@ test_expect_success 'status --long --branch' '
                git checkout b1 >/dev/null &&
                git status --long -b | head -3
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'status --long --branch' '
@@ -196,7 +196,7 @@ test_expect_success 'status --long --branch' '
                git checkout b1 >/dev/null &&
                git -c status.aheadbehind=true status --long -b | head -3
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -210,7 +210,7 @@ test_expect_success 'status --long --branch --no-ahead-behind' '
                git checkout b1 >/dev/null &&
                git status --long -b --no-ahead-behind | head -2
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'status.aheadbehind=false status --long --branch' '
@@ -219,7 +219,7 @@ test_expect_success 'status.aheadbehind=false status --long --branch' '
                git checkout b1 >/dev/null &&
                git -c status.aheadbehind=false status --long -b | head -2
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -232,7 +232,7 @@ test_expect_success 'status -s -b (upstream is gone)' '
                git checkout b5 >/dev/null &&
                git status -s -b | head -1
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<\EOF
@@ -245,7 +245,7 @@ test_expect_success 'status -s -b (up-to-date with upstream)' '
                git checkout b6 >/dev/null &&
                git status -s -b | head -1
        ) >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'fail to track lightweight tags' '
index 2b551e6fd0ce1d6706334eaa2a65cda7f9e56618..3f889949ca14dc77eb6b39e5246bea3fe6823fd8 100755 (executable)
@@ -2,6 +2,7 @@
 
 test_description='rev-list combining bitmaps and filters'
 . ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-bitmap.sh
 
 test_expect_success 'set up bitmapped repo' '
        # one commit will have bitmaps, the other will not
diff --git a/t/t6115-rev-list-du.sh b/t/t6115-rev-list-du.sh
new file mode 100755 (executable)
index 0000000..b4aef32
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh
+
+test_description='basic tests of rev-list --disk-usage'
+. ./test-lib.sh
+
+# we want a mix of reachable and unreachable, as well as
+# objects in the bitmapped pack and some outside of it
+test_expect_success 'set up repository' '
+       test_commit --no-tag one &&
+       test_commit --no-tag two &&
+       git repack -adb &&
+       git reset --hard HEAD^ &&
+       test_commit --no-tag three &&
+       test_commit --no-tag four &&
+       git reset --hard HEAD^
+'
+
+# We don't want to hardcode sizes, because they depend on the exact details of
+# packing, zlib, etc. We'll assume that the regular rev-list and cat-file
+# machinery works and compare the --disk-usage output to that.
+disk_usage_slow () {
+       git rev-list --no-object-names "$@" |
+       git cat-file --batch-check="%(objectsize:disk)" |
+       perl -lne '$total += $_; END { print $total}'
+}
+
+# check behavior with given rev-list options; note that
+# whitespace is not preserved in args
+check_du () {
+       args=$*
+
+       test_expect_success "generate expected size ($args)" "
+               disk_usage_slow $args >expect
+       "
+
+       test_expect_success "rev-list --disk-usage without bitmaps ($args)" "
+               git rev-list --disk-usage $args >actual &&
+               test_cmp expect actual
+       "
+
+       test_expect_success "rev-list --disk-usage with bitmaps ($args)" "
+               git rev-list --disk-usage --use-bitmap-index $args >actual &&
+               test_cmp expect actual
+       "
+}
+
+check_du HEAD
+check_du --objects HEAD
+check_du --objects HEAD^..HEAD
+
+test_done
index 546796f84732c5153444321dc5a1d34bbf8af878..e89b6747beec4fb6a98b9a54a21bb62082cb0c91 100755 (executable)
@@ -136,7 +136,7 @@ warning: tag 'Q' is externally known as 'A'
 EOF
 check_describe A-* HEAD
 test_expect_success 'warning was displayed for Q' '
-       test_i18ncmp err.expect err.actual
+       test_cmp err.expect err.actual
 '
 test_expect_success 'misnamed annotated tag forces long output' '
        description=$(git describe --no-long Q^0) &&
index c670668409817c6d1066de53dc294bade2f27e93..0f1cb49cedc645c5a9b0c56257d3aca4740d5cd3 100755 (executable)
@@ -21,7 +21,7 @@ EOF
 test_expect_success 'error message for path inside submodule' '
        echo a >sub/a &&
        test_must_fail git add sub/a 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'error message for path inside submodule from within submodule' '
index 809854fc0ce98e16d8d4faa1ae22715134293c84..40edf9dab53402db9b5fad6dd060768537f9ba9a 100755 (executable)
@@ -20,8 +20,8 @@ test_expect_success 'Broken refs are reported correctly' '
        test_when_finished "rm -f .git/$r" &&
        echo "warning: ignoring broken ref $r" >broken-err &&
        git for-each-ref >out 2>err &&
-       test_i18ncmp full-list out &&
-       test_i18ncmp broken-err err
+       test_cmp full-list out &&
+       test_cmp broken-err err
 '
 
 test_expect_success 'NULL_SHA1 refs are reported correctly' '
@@ -31,10 +31,10 @@ test_expect_success 'NULL_SHA1 refs are reported correctly' '
        echo "warning: ignoring broken ref $r" >zeros-err &&
        git for-each-ref >out 2>err &&
        test_cmp full-list out &&
-       test_i18ncmp zeros-err err &&
+       test_cmp zeros-err err &&
        git for-each-ref --format="%(objectname) %(refname)" >brief-out 2>brief-err &&
        test_cmp brief-list brief-out &&
-       test_i18ncmp zeros-err brief-err
+       test_cmp zeros-err brief-err
 '
 
 test_expect_success 'Missing objects are reported correctly' '
@@ -43,7 +43,7 @@ test_expect_success 'Missing objects are reported correctly' '
        test_when_finished "rm -f .git/$r" &&
        echo "fatal: missing object $MISSING for $r" >missing-err &&
        test_must_fail git for-each-ref 2>err &&
-       test_i18ncmp missing-err err &&
+       test_cmp missing-err err &&
        (
                cat brief-list &&
                echo "$MISSING $r"
index c7ab7048f589ec506888135960138dd4c11ca282..eaf48e941e2a3934fde54ac9bdc22f96a7a035cd 100755 (executable)
@@ -18,6 +18,8 @@ GIT_COMMITTER_DATE="2006-12-12 23:28:00 +0100"
 export GIT_COMMITTER_DATE
 
 test_expect_success 'setup tests' '
+       GIT_TEST_COMMIT_GRAPH=0 &&
+       export GIT_TEST_COMMIT_GRAPH &&
        echo 1 >a1 &&
        git add a1 &&
        GIT_AUTHOR_DATE="2006-12-12 23:00:00" git commit -m 1 a1 &&
@@ -69,7 +71,7 @@ test_expect_success 'setup tests' '
 '
 
 test_expect_success 'combined merge conflicts' '
-       test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git merge -m final G
+       test_must_fail git merge -m final G
 '
 
 test_expect_success 'result contains a conflict' '
@@ -85,6 +87,7 @@ test_expect_success 'result contains a conflict' '
 '
 
 test_expect_success 'virtual trees were processed' '
+       # TODO: fragile test, relies on ambigious merge-base resolution
        git ls-files --stage >out &&
 
        cat >expect <<-EOF &&
index 4ab133f489ca322a752599dc940e4c9c12461ad3..5d3b711fe682d88601d3d722e7ab1ee27ac9b525 100755 (executable)
@@ -2905,7 +2905,7 @@ test_setup_9e () {
        )
 }
 
-test_expect_success C_LOCALE_OUTPUT '9e: N-to-1 whammo' '
+test_expect_success '9e: N-to-1 whammo' '
        test_setup_9e &&
        (
                cd 9e &&
index d7eeee431061c4195b91a86581fd8462551ceb42..7b5f1c1dcd1f68c74646a5983b6b0d53bd9b4a92 100755 (executable)
@@ -492,7 +492,9 @@ test_expect_success '3a-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
                test_cmp expect actual &&
 
                test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-               test_path_is_missing bq foo/bq foo/whatever
+               test_path_is_missing bq &&
+               test_path_is_missing foo/bq &&
+               test_path_is_missing foo/whatever
        )
 '
 
@@ -522,7 +524,9 @@ test_expect_success '3a-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
                test_cmp expect actual &&
 
                test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-               test_path_is_missing bq foo/bq foo/whatever
+               test_path_is_missing bq &&
+               test_path_is_missing foo/bq &&
+               test_path_is_missing foo/whatever
        )
 '
 
@@ -588,7 +592,9 @@ test_expect_success '3b-L: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
                test_cmp expect actual &&
 
                test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-               test_path_is_missing bq foo/bq foo/whatever
+               test_path_is_missing bq &&
+               test_path_is_missing foo/bq &&
+               test_path_is_missing foo/whatever
        )
 '
 
@@ -618,7 +624,9 @@ test_expect_success '3b-R: bq_1->foo/bq_2 on A, foo/->bar/ on B' '
                test_cmp expect actual &&
 
                test_must_fail git rev-parse HEAD:bq HEAD:foo/bq &&
-               test_path_is_missing bq foo/bq foo/whatever
+               test_path_is_missing bq &&
+               test_path_is_missing foo/bq &&
+               test_path_is_missing foo/whatever
        )
 '
 
index 362ae37a122f15d08cc7f8301c828139d5fbc0ac..84b4aacf496d1df73c6adf83860db430bc1c1c4c 100755 (executable)
@@ -139,7 +139,7 @@ test_expect_success 'will not overwrite untracked file in leading path' '
        cp important sub &&
        cp important sub2 &&
        test_must_fail git merge sub 2>out &&
-       test_i18ncmp out expect &&
+       test_cmp out expect &&
        test_path_is_missing .git/MERGE_HEAD &&
        test_cmp important sub &&
        test_cmp important sub2 &&
@@ -174,7 +174,7 @@ test_expect_success 'will not overwrite untracked file on unborn branch' '
        git checkout --orphan new &&
        cp important c0.c &&
        test_must_fail git merge c0 2>out &&
-       test_i18ncmp out expect
+       test_cmp out expect
 '
 
 test_expect_success 'will not overwrite untracked file on unborn branch .git/MERGE_HEAD sanity etc.' '
index e176475ed5bbe1ca9a10e0f533add9a8234b7b56..5bfb027099a6d22849739075de82f3c0af7ca513 100755 (executable)
@@ -40,14 +40,14 @@ EOF
 
 test_expect_success 'untracked files overwritten by merge (fast and non-fast forward)' '
        test_must_fail git merge branch 2>out &&
-       test_i18ncmp out expect &&
+       test_cmp out expect &&
        git commit --allow-empty -m empty &&
        (
                GIT_MERGE_VERBOSITY=0 &&
                export GIT_MERGE_VERBOSITY &&
                test_must_fail git merge branch 2>out2
        ) &&
-       test_i18ncmp out2 expect &&
+       test_cmp out2 expect &&
        git reset --hard HEAD^
 '
 
@@ -68,7 +68,7 @@ test_expect_success 'untracked files or local changes ovewritten by merge' '
        git add three &&
        git add four &&
        test_must_fail git merge branch 2>out &&
-       test_i18ncmp out expect
+       test_cmp out expect
 '
 
 cat >expect <<\EOF
@@ -90,7 +90,7 @@ test_expect_success 'cannot switch branches because of local changes' '
        echo uno >rep/one &&
        echo dos >rep/two &&
        test_must_fail git checkout branch 2>out &&
-       test_i18ncmp out expect
+       test_cmp out expect
 '
 
 cat >expect <<\EOF
@@ -104,7 +104,7 @@ EOF
 test_expect_success 'not uptodate file porcelain checkout error' '
        git add rep/one rep/two &&
        test_must_fail git checkout branch 2>out &&
-       test_i18ncmp out expect
+       test_cmp out expect
 '
 
 cat >expect <<\EOF
@@ -135,7 +135,7 @@ test_expect_success 'not_uptodate_dir porcelain checkout error' '
        >rep/untracked-file &&
        >rep2/untracked-file &&
        test_must_fail git checkout branch 2>out &&
-       test_i18ncmp out ../expect
+       test_cmp out ../expect
 '
 
 test_done
index 4a3b8f48ac638d05759377524c0e9566e36e94a9..60d961b52600548fc121fac4183728f86bde05af 100755 (executable)
@@ -78,7 +78,7 @@ test_expect_success 'gc --keep-largest-pack' '
                git gc &&
                ( cd .git/objects/pack && ls *.pack ) >pack-list &&
                test_line_count = 1 pack-list &&
-               BASE_PACK=.git/objects/pack/pack-*.pack &&
+               cp pack-list base-pack-list &&
                test_commit four &&
                git repack -d &&
                test_commit five &&
@@ -90,7 +90,7 @@ test_expect_success 'gc --keep-largest-pack' '
                test_line_count = 2 pack-list &&
                awk "/^P /{print \$2}" <.git/objects/info/packs >pack-info &&
                test_line_count = 2 pack-info &&
-               test_path_is_file $BASE_PACK &&
+               test_path_is_file .git/objects/pack/$(cat base-pack-list) &&
                git fsck
        )
 '
@@ -106,17 +106,17 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
        test_commit "$(test_oid obj2)" &&
        # Our first gc will create a pack; our second will create a second pack
        git gc --auto &&
-       ls .git/objects/pack | sort >existing_packs &&
+       ls .git/objects/pack/pack-*.pack | sort >existing_packs &&
        test_commit "$(test_oid obj3)" &&
        test_commit "$(test_oid obj4)" &&
 
        git gc --auto 2>err &&
        test_i18ngrep ! "^warning:" err &&
-       ls .git/objects/pack/ | sort >post_packs &&
+       ls .git/objects/pack/pack-*.pack | sort >post_packs &&
        comm -1 -3 existing_packs post_packs >new &&
        comm -2 -3 existing_packs post_packs >del &&
        test_line_count = 0 del && # No packs are deleted
-       test_line_count = 2 new # There is one new pack and its .idx
+       test_line_count = 1 new # There is one new pack
 '
 
 test_expect_success 'gc --no-quiet' '
index f807276337d83c74be636eb58cd1203105c24d04..e2d33a8a4c46124fcdf2c3ee69554c5ecf2af774 100755 (executable)
@@ -55,10 +55,13 @@ test_expect_success 'setup' '
        git show-ref -s commit-5-5 | git commit-graph write --stdin-commits &&
        mv .git/objects/info/commit-graph commit-graph-half &&
        chmod u+w commit-graph-half &&
+       GIT_TEST_COMMIT_GRAPH_NO_GDAT=1 git commit-graph write --reachable &&
+       mv .git/objects/info/commit-graph commit-graph-no-gdat &&
+       chmod u+w commit-graph-no-gdat &&
        git config core.commitGraph true
 '
 
-run_three_modes () {
+run_all_modes () {
        test_when_finished rm -rf .git/objects/info/commit-graph &&
        "$@" <input >actual &&
        test_cmp expect actual &&
@@ -67,11 +70,14 @@ run_three_modes () {
        test_cmp expect actual &&
        cp commit-graph-half .git/objects/info/commit-graph &&
        "$@" <input >actual &&
+       test_cmp expect actual &&
+       cp commit-graph-no-gdat .git/objects/info/commit-graph &&
+       "$@" <input >actual &&
        test_cmp expect actual
 }
 
-test_three_modes () {
-       run_three_modes test-tool reach "$@"
+test_all_modes () {
+       run_all_modes test-tool reach "$@"
 }
 
 test_expect_success 'ref_newer:miss' '
@@ -80,7 +86,7 @@ test_expect_success 'ref_newer:miss' '
        B:commit-4-9
        EOF
        echo "ref_newer(A,B):0" >expect &&
-       test_three_modes ref_newer
+       test_all_modes ref_newer
 '
 
 test_expect_success 'ref_newer:hit' '
@@ -89,7 +95,7 @@ test_expect_success 'ref_newer:hit' '
        B:commit-2-3
        EOF
        echo "ref_newer(A,B):1" >expect &&
-       test_three_modes ref_newer
+       test_all_modes ref_newer
 '
 
 test_expect_success 'in_merge_bases:hit' '
@@ -98,7 +104,7 @@ test_expect_success 'in_merge_bases:hit' '
        B:commit-8-8
        EOF
        echo "in_merge_bases(A,B):1" >expect &&
-       test_three_modes in_merge_bases
+       test_all_modes in_merge_bases
 '
 
 test_expect_success 'in_merge_bases:miss' '
@@ -107,7 +113,7 @@ test_expect_success 'in_merge_bases:miss' '
        B:commit-5-9
        EOF
        echo "in_merge_bases(A,B):0" >expect &&
-       test_three_modes in_merge_bases
+       test_all_modes in_merge_bases
 '
 
 test_expect_success 'in_merge_bases_many:hit' '
@@ -117,7 +123,7 @@ test_expect_success 'in_merge_bases_many:hit' '
        X:commit-5-7
        EOF
        echo "in_merge_bases_many(A,X):1" >expect &&
-       test_three_modes in_merge_bases_many
+       test_all_modes in_merge_bases_many
 '
 
 test_expect_success 'in_merge_bases_many:miss' '
@@ -127,7 +133,7 @@ test_expect_success 'in_merge_bases_many:miss' '
        X:commit-8-6
        EOF
        echo "in_merge_bases_many(A,X):0" >expect &&
-       test_three_modes in_merge_bases_many
+       test_all_modes in_merge_bases_many
 '
 
 test_expect_success 'in_merge_bases_many:miss-heuristic' '
@@ -137,7 +143,7 @@ test_expect_success 'in_merge_bases_many:miss-heuristic' '
        X:commit-6-6
        EOF
        echo "in_merge_bases_many(A,X):0" >expect &&
-       test_three_modes in_merge_bases_many
+       test_all_modes in_merge_bases_many
 '
 
 test_expect_success 'is_descendant_of:hit' '
@@ -148,7 +154,7 @@ test_expect_success 'is_descendant_of:hit' '
        X:commit-1-1
        EOF
        echo "is_descendant_of(A,X):1" >expect &&
-       test_three_modes is_descendant_of
+       test_all_modes is_descendant_of
 '
 
 test_expect_success 'is_descendant_of:miss' '
@@ -159,7 +165,7 @@ test_expect_success 'is_descendant_of:miss' '
        X:commit-7-6
        EOF
        echo "is_descendant_of(A,X):0" >expect &&
-       test_three_modes is_descendant_of
+       test_all_modes is_descendant_of
 '
 
 test_expect_success 'get_merge_bases_many' '
@@ -174,7 +180,7 @@ test_expect_success 'get_merge_bases_many' '
                git rev-parse commit-5-6 \
                              commit-4-7 | sort
        } >expect &&
-       test_three_modes get_merge_bases_many
+       test_all_modes get_merge_bases_many
 '
 
 test_expect_success 'reduce_heads' '
@@ -196,7 +202,7 @@ test_expect_success 'reduce_heads' '
                              commit-2-8 \
                              commit-1-10 | sort
        } >expect &&
-       test_three_modes reduce_heads
+       test_all_modes reduce_heads
 '
 
 test_expect_success 'can_all_from_reach:hit' '
@@ -219,7 +225,7 @@ test_expect_success 'can_all_from_reach:hit' '
        Y:commit-8-1
        EOF
        echo "can_all_from_reach(X,Y):1" >expect &&
-       test_three_modes can_all_from_reach
+       test_all_modes can_all_from_reach
 '
 
 test_expect_success 'can_all_from_reach:miss' '
@@ -241,7 +247,7 @@ test_expect_success 'can_all_from_reach:miss' '
        Y:commit-8-5
        EOF
        echo "can_all_from_reach(X,Y):0" >expect &&
-       test_three_modes can_all_from_reach
+       test_all_modes can_all_from_reach
 '
 
 test_expect_success 'can_all_from_reach_with_flag: tags case' '
@@ -264,7 +270,7 @@ test_expect_success 'can_all_from_reach_with_flag: tags case' '
        Y:commit-8-1
        EOF
        echo "can_all_from_reach_with_flag(X,_,_,0,0):1" >expect &&
-       test_three_modes can_all_from_reach_with_flag
+       test_all_modes can_all_from_reach_with_flag
 '
 
 test_expect_success 'commit_contains:hit' '
@@ -280,8 +286,8 @@ test_expect_success 'commit_contains:hit' '
        X:commit-9-3
        EOF
        echo "commit_contains(_,A,X,_):1" >expect &&
-       test_three_modes commit_contains &&
-       test_three_modes commit_contains --tag
+       test_all_modes commit_contains &&
+       test_all_modes commit_contains --tag
 '
 
 test_expect_success 'commit_contains:miss' '
@@ -297,8 +303,8 @@ test_expect_success 'commit_contains:miss' '
        X:commit-9-3
        EOF
        echo "commit_contains(_,A,X,_):0" >expect &&
-       test_three_modes commit_contains &&
-       test_three_modes commit_contains --tag
+       test_all_modes commit_contains &&
+       test_all_modes commit_contains --tag
 '
 
 test_expect_success 'rev-list: basic topo-order' '
@@ -310,7 +316,7 @@ test_expect_success 'rev-list: basic topo-order' '
                commit-6-2 commit-5-2 commit-4-2 commit-3-2 commit-2-2 commit-1-2 \
                commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
        >expect &&
-       run_three_modes git rev-list --topo-order commit-6-6
+       run_all_modes git rev-list --topo-order commit-6-6
 '
 
 test_expect_success 'rev-list: first-parent topo-order' '
@@ -322,7 +328,7 @@ test_expect_success 'rev-list: first-parent topo-order' '
                commit-6-2 \
                commit-6-1 commit-5-1 commit-4-1 commit-3-1 commit-2-1 commit-1-1 \
        >expect &&
-       run_three_modes git rev-list --first-parent --topo-order commit-6-6
+       run_all_modes git rev-list --first-parent --topo-order commit-6-6
 '
 
 test_expect_success 'rev-list: range topo-order' '
@@ -334,7 +340,7 @@ test_expect_success 'rev-list: range topo-order' '
                commit-6-2 commit-5-2 commit-4-2 \
                commit-6-1 commit-5-1 commit-4-1 \
        >expect &&
-       run_three_modes git rev-list --topo-order commit-3-3..commit-6-6
+       run_all_modes git rev-list --topo-order commit-3-3..commit-6-6
 '
 
 test_expect_success 'rev-list: range topo-order' '
@@ -346,7 +352,7 @@ test_expect_success 'rev-list: range topo-order' '
                commit-6-2 commit-5-2 commit-4-2 \
                commit-6-1 commit-5-1 commit-4-1 \
        >expect &&
-       run_three_modes git rev-list --topo-order commit-3-8..commit-6-6
+       run_all_modes git rev-list --topo-order commit-3-8..commit-6-6
 '
 
 test_expect_success 'rev-list: first-parent range topo-order' '
@@ -358,7 +364,7 @@ test_expect_success 'rev-list: first-parent range topo-order' '
                commit-6-2 \
                commit-6-1 commit-5-1 commit-4-1 \
        >expect &&
-       run_three_modes git rev-list --first-parent --topo-order commit-3-8..commit-6-6
+       run_all_modes git rev-list --first-parent --topo-order commit-3-8..commit-6-6
 '
 
 test_expect_success 'rev-list: ancestry-path topo-order' '
@@ -368,7 +374,7 @@ test_expect_success 'rev-list: ancestry-path topo-order' '
                commit-6-4 commit-5-4 commit-4-4 commit-3-4 \
                commit-6-3 commit-5-3 commit-4-3 \
        >expect &&
-       run_three_modes git rev-list --topo-order --ancestry-path commit-3-3..commit-6-6
+       run_all_modes git rev-list --topo-order --ancestry-path commit-3-3..commit-6-6
 '
 
 test_expect_success 'rev-list: symmetric difference topo-order' '
@@ -382,7 +388,7 @@ test_expect_success 'rev-list: symmetric difference topo-order' '
                commit-3-8 commit-2-8 commit-1-8 \
                commit-3-7 commit-2-7 commit-1-7 \
        >expect &&
-       run_three_modes git rev-list --topo-order commit-3-8...commit-6-6
+       run_all_modes git rev-list --topo-order commit-3-8...commit-6-6
 '
 
 test_expect_success 'get_reachable_subset:all' '
@@ -402,7 +408,7 @@ test_expect_success 'get_reachable_subset:all' '
                              commit-1-7 \
                              commit-5-6 | sort
        ) >expect &&
-       test_three_modes get_reachable_subset
+       test_all_modes get_reachable_subset
 '
 
 test_expect_success 'get_reachable_subset:some' '
@@ -420,7 +426,7 @@ test_expect_success 'get_reachable_subset:some' '
                git rev-parse commit-3-3 \
                              commit-1-7 | sort
        ) >expect &&
-       test_three_modes get_reachable_subset
+       test_all_modes get_reachable_subset
 '
 
 test_expect_success 'get_reachable_subset:none' '
@@ -434,7 +440,7 @@ test_expect_success 'get_reachable_subset:none' '
        Y:commit-2-8
        EOF
        echo "get_reachable_subset(X,Y)" >expect &&
-       test_three_modes get_reachable_subset
+       test_all_modes get_reachable_subset
 '
 
 test_done
index 63d5f41a1247839412b9c6022fb3f4a07c4e6b0e..080a65308316b1f03ba57e9bbcc34cb3feb504a9 100755 (executable)
@@ -446,7 +446,7 @@ test_expect_success 'mv issues a warning when section is not found in .gitmodule
        echo "warning: Could not find section in .gitmodules where path=sub" >expect.err &&
        mkdir mod &&
        git mv sub mod/sub 2>actual.err &&
-       test_i18ncmp expect.err actual.err &&
+       test_cmp expect.err actual.err &&
        ! test -e sub &&
        [ "$entry" = "$(git ls-files --stage mod/sub | cut -f 1)" ] &&
        (
index 943a7d5c1db9563eb8ed648f0f760a54b9df58eb..2f72c5c6883e3e45bbc9602f3e85e1dd9ae3fd01 100755 (executable)
@@ -20,6 +20,13 @@ tag_exists () {
        git show-ref --quiet --verify refs/tags/"$1"
 }
 
+test_expect_success 'setup' '
+       test_oid_cache <<-EOM
+       othersigheader sha1:gpgsig-sha256
+       othersigheader sha256:gpgsig
+       EOM
+'
+
 test_expect_success 'listing all tags in an empty tree should succeed' '
        git tag -l &&
        git tag
@@ -91,7 +98,7 @@ test_expect_success 'creating a tag with --create-reflog should create reflog' '
        git tag --create-reflog tag_with_reflog &&
        git reflog exists refs/tags/tag_with_reflog &&
        sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'annotated tag with --create-reflog has correct message' '
@@ -102,7 +109,7 @@ test_expect_success 'annotated tag with --create-reflog has correct message' '
        git tag -m "annotated tag" --create-reflog tag_with_reflog &&
        git reflog exists refs/tags/tag_with_reflog &&
        sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success '--create-reflog does not create reflog on failure' '
@@ -1374,6 +1381,24 @@ test_expect_success GPG \
        'test_config gpg.program echo &&
         test_must_fail git tag -s -m tail tag-gpg-failure'
 
+# try to produce invalid signature
+test_expect_success GPG 'git verifies tag is valid with double signature' '
+       git tag -s -m tail tag-gpg-double-sig &&
+       git cat-file tag tag-gpg-double-sig >tag &&
+       othersigheader=$(test_oid othersigheader) &&
+       sed -ne "/^\$/q;p" tag >new-tag &&
+       cat <<-EOM >>new-tag &&
+       $othersigheader -----BEGIN PGP SIGNATURE-----
+        someinvaliddata
+        -----END PGP SIGNATURE-----
+       EOM
+       sed -e "1,/^tagger/d" tag >>new-tag &&
+       new_tag=$(git hash-object -t tag -w new-tag) &&
+       git update-ref refs/tags/tag-gpg-double-sig $new_tag &&
+       git verify-tag tag-gpg-double-sig &&
+       git fsck
+'
+
 # try to sign with bad user.signingkey
 test_expect_success GPGSM \
        'git tag -s fails if gpgsm is misconfigured (bad key)' \
@@ -1409,7 +1434,7 @@ test_expect_success 'message in editor has initial comment: first line' '
        # check the first line --- should be empty
        echo >first.expect &&
        sed -e 1q <actual >first.actual &&
-       test_i18ncmp first.expect first.actual
+       test_cmp first.expect first.actual
 '
 
 test_expect_success \
@@ -1732,7 +1757,7 @@ test_expect_success 'recursive tagging should give advice' '
        hint: Disable this message with "git config advice.nestedTag false"
        EOF
        git tag -m nested nested annotated-v4.0 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'multiple --points-at are OR-ed together' '
index fdb450e446a987717d4565ef58cccfa332259927..0e7cf75435eca107fc17b836fa939d3378e983a4 100755 (executable)
@@ -656,4 +656,134 @@ test_expect_success TTY 'git tag with auto-columns ' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup trace2' '
+       GIT_TRACE2_BRIEF=1 &&
+       export GIT_TRACE2_BRIEF
+'
+
+test_expect_success TTY 'git returns SIGPIPE on early pager exit' '
+       test_when_finished "rm pager-used trace.normal" &&
+       test_config core.pager ">pager-used; head -n 1; exit 0" &&
+       GIT_TRACE2="$(pwd)/trace.normal" &&
+       export GIT_TRACE2 &&
+       test_when_finished "unset GIT_TRACE2" &&
+
+       if test_have_prereq !MINGW
+       then
+               OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+               test_match_signal 13 "$OUT"
+       else
+               test_terminal git log
+       fi &&
+
+       grep child_exit trace.normal >child-exits &&
+       test_line_count = 1 child-exits &&
+       grep " code:0 " child-exits &&
+       test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git returns SIGPIPE on early pager non-zero exit' '
+       test_when_finished "rm pager-used trace.normal" &&
+       test_config core.pager ">pager-used; head -n 1; exit 1" &&
+       GIT_TRACE2="$(pwd)/trace.normal" &&
+       export GIT_TRACE2 &&
+       test_when_finished "unset GIT_TRACE2" &&
+
+       if test_have_prereq !MINGW
+       then
+               OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+               test_match_signal 13 "$OUT"
+       else
+               test_terminal git log
+       fi &&
+
+       grep child_exit trace.normal >child-exits &&
+       test_line_count = 1 child-exits &&
+       grep " code:1 " child-exits &&
+       test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git discards pager non-zero exit without SIGPIPE' '
+       test_when_finished "rm pager-used trace.normal" &&
+       test_config core.pager "wc >pager-used; exit 1" &&
+       GIT_TRACE2="$(pwd)/trace.normal" &&
+       export GIT_TRACE2 &&
+       test_when_finished "unset GIT_TRACE2" &&
+
+       if test_have_prereq !MINGW
+       then
+               OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+               test "$OUT" -eq 0
+       else
+               test_terminal git log
+       fi &&
+
+       grep child_exit trace.normal >child-exits &&
+       test_line_count = 1 child-exits &&
+       grep " code:1 " child-exits &&
+       test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git discards nonexisting pager without SIGPIPE' '
+       test_when_finished "rm pager-used trace.normal" &&
+       test_config core.pager "wc >pager-used; does-not-exist" &&
+       GIT_TRACE2="$(pwd)/trace.normal" &&
+       export GIT_TRACE2 &&
+       test_when_finished "unset GIT_TRACE2" &&
+
+       if test_have_prereq !MINGW
+       then
+               OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+               test "$OUT" -eq 0
+       else
+               test_terminal git log
+       fi &&
+
+       grep child_exit trace.normal >child-exits &&
+       test_line_count = 1 child-exits &&
+       grep " code:127 " child-exits &&
+       test_path_is_file pager-used
+'
+
+test_expect_success TTY 'git attempts to page to nonexisting pager command, gets SIGPIPE' '
+       test_when_finished "rm trace.normal" &&
+       test_config core.pager "does-not-exist" &&
+       GIT_TRACE2="$(pwd)/trace.normal" &&
+       export GIT_TRACE2 &&
+       test_when_finished "unset GIT_TRACE2" &&
+
+       if test_have_prereq !MINGW
+       then
+               OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+               test_match_signal 13 "$OUT"
+       else
+               test_terminal git log
+       fi &&
+
+       grep child_exit trace.normal >child-exits &&
+       test_line_count = 1 child-exits &&
+       grep " code:-1 " child-exits
+'
+
+test_expect_success TTY 'git returns SIGPIPE on propagated signals from pager' '
+       test_when_finished "rm pager-used trace.normal" &&
+       test_config core.pager ">pager-used; test-tool sigchain" &&
+       GIT_TRACE2="$(pwd)/trace.normal" &&
+       export GIT_TRACE2 &&
+       test_when_finished "unset GIT_TRACE2" &&
+
+       if test_have_prereq !MINGW
+       then
+               OUT=$( ((test_terminal git log; echo $? 1>&3) | :) 3>&1 ) &&
+               test_match_signal 13 "$OUT"
+       else
+               test_terminal git log
+       fi &&
+
+       grep child_exit trace.normal >child-exits &&
+       test_line_count = 1 child-exits &&
+       grep " code:143 " child-exits &&
+       test_path_is_file pager-used
+'
+
 test_done
index e5c6a038fbfccb3da7c3c179fa0c6c186de68d40..f2a8e765114fd1a01f1256a6768353cd2ab69b4a 100755 (executable)
@@ -125,13 +125,13 @@ EOF
 test_expect_success 'git-clean, absent case' '
        setup_absent &&
        git clean -n > result &&
-       test_i18ncmp expected result
+       test_cmp expected result
 '
 
 test_expect_success 'git-clean, dirty case' '
        setup_dirty &&
        git clean -n > result &&
-       test_i18ncmp expected result
+       test_cmp expected result
 '
 
 test_expect_success '--ignore-skip-worktree-entries leaves worktree alone' '
index 72fb418b8904ffedac1fcf87d97878c86e054b5b..0f4344c55e6421d605ab7364bc2fedfe9165b02b 100755 (executable)
@@ -56,9 +56,9 @@ EOF
                git commit -m delete &&
                test_must_fail git merge main &&
                test_must_fail git commit --dry-run >../actual &&
-               test_i18ncmp ../expect ../actual &&
+               test_cmp ../expect ../actual &&
                git status >../actual &&
-               test_i18ncmp ../expect ../actual
+               test_cmp ../expect ../actual
        )
 '
 
@@ -151,7 +151,7 @@ Unmerged paths:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -185,7 +185,7 @@ Unmerged paths:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -210,7 +210,7 @@ Unmerged paths:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual &&
+       test_cmp expected actual &&
        git reset --hard &&
        git checkout main
 '
@@ -227,7 +227,7 @@ test_expect_success 'status --branch with detached HEAD' '
        ?? expected
        ?? mdconflict/
        EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 ## Duplicate the above test and verify --porcelain=v1 arg parsing.
@@ -243,7 +243,7 @@ test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '
        ?? expected
        ?? mdconflict/
        EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 ## Verify parser error on invalid --porcelain argument.
index f01bf27727905db64f5d7eeaca5fd05b5e90f02f..accefde72fb1bd518b2fcd7caf4d8c4921180bdc 100755 (executable)
@@ -731,7 +731,7 @@ test_expect_success 'test ident field is working' '
        cp -R done dthree dtwo four three ../other_worktree &&
        GIT_WORK_TREE=../other_worktree git status 2>../err &&
        echo "warning: untracked cache is disabled on this system or location" >../expect &&
-       test_i18ncmp ../expect ../err
+       test_cmp ../expect ../err
 '
 
 test_expect_success 'untracked cache survives a checkout' '
index 7d8fb188ee5eb914cc77e30846191adb78adb5bb..601b2bf97f0e7f40de454358b6a7975488644687 100755 (executable)
@@ -75,14 +75,14 @@ test_expect_success 'reset --hard message' '
        hex=$(git log -1 --format="%h") &&
        git reset --hard >.actual &&
        echo HEAD is now at $hex $(commit_msg) >.expected &&
-       test_i18ncmp .expected .actual
+       test_cmp .expected .actual
 '
 
 test_expect_success 'reset --hard message (ISO8859-1 logoutputencoding)' '
        hex=$(git log -1 --format="%h") &&
        git -c "i18n.logOutputEncoding=$test_encoding" reset --hard >.actual &&
        echo HEAD is now at $hex $(commit_msg $test_encoding) >.expected &&
-       test_i18ncmp .expected .actual
+       test_cmp .expected .actual
 '
 
 test_expect_success 'giving a non existing revision should fail' '
@@ -469,7 +469,7 @@ test_expect_success '--mixed refreshes the index' '
        EOF
        echo 123 >>file2 &&
        git reset --mixed HEAD >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'resetting specific path that is unmerged' '
index cb5e34d94c3a163ecc3785d4b3afdeeb98c60ec6..a74816ca8b466c789c3bc8b17e629d5edcbb25e9 100755 (executable)
@@ -110,7 +110,7 @@ test_expect_success 'git clean with prefix' '
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
+test_expect_success 'git clean with relative prefix' '
 
        mkdir -p build docs &&
        touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -123,7 +123,7 @@ test_expect_success C_LOCALE_OUTPUT 'git clean with relative prefix' '
        verbose test "$would_clean" = ../src/part3.c
 '
 
-test_expect_success C_LOCALE_OUTPUT 'git clean with absolute path' '
+test_expect_success 'git clean with absolute path' '
 
        mkdir -p build docs &&
        touch a.out src/part3.c docs/manual.txt obj.o build/lib.so &&
@@ -407,7 +407,7 @@ test_expect_success 'clean.requireForce and -f' '
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'core.excludesfile' '
+test_expect_success 'core.excludesfile' '
 
        echo excludes >excludes &&
        echo included >included &&
index d44f69629337f7e617d118e060550737ca92c59a..a924fdb7a6c9aaf6c1b860dd4c783d87b803c36f 100755 (executable)
@@ -55,7 +55,7 @@ test_expect_success 'add aborts on repository with no commits' '
        EOF
        git init repo-no-commits &&
        test_must_fail git submodule add ../a ./repo-no-commits 2>actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'status should ignore inner git repo when not added' '
@@ -185,7 +185,7 @@ test_expect_success 'submodule add to .gitignored path fails' '
                git add --force .gitignore &&
                git commit -m"Ignore everything" &&
                ! git submodule add "$submodurl" submod >actual 2>&1 &&
-               test_i18ncmp expect actual
+               test_cmp expect actual
        )
 '
 
index 76088147089c9bacdf0cb9f7cb570c55c1ec3f4d..9c3cc4cf4046befd76e0239c831d61b33196ee86 100755 (executable)
@@ -190,7 +190,7 @@ test_expect_success 'typechanged submodule(submodule->blob), --cached' "
          < Add foo5
 
        EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 "
 
 test_expect_success 'typechanged submodule(submodule->blob), --files' "
@@ -200,7 +200,7 @@ test_expect_success 'typechanged submodule(submodule->blob), --files' "
          > Add foo5
 
        EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 "
 
 rm -rf sm1 &&
@@ -211,7 +211,7 @@ test_expect_success 'typechanged submodule(submodule->blob)' "
        * sm1 $head4(submodule)->$head5(blob):
 
        EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 "
 
 rm -f sm1 &&
@@ -224,7 +224,7 @@ test_expect_success 'nonexistent commit' "
          Warn: sm1 doesn't contain commit $head4_full
 
        EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 "
 
 commit_file
@@ -235,7 +235,7 @@ test_expect_success 'typechanged submodule(blob->submodule)' "
          > Add foo7
 
        EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 "
 
 commit_file sm1 &&
@@ -292,7 +292,7 @@ test_expect_success 'given commit' "
 
 test_expect_success '--for-status' "
        git submodule summary --for-status HEAD^ >actual &&
-       test_i18ncmp - actual <<-EOF
+       test_cmp - actual <<-EOF
        * sm1 $head6...0000000:
 
        * sm2 0000000...$head7 (2):
index b9c1624fba9fda209f7e86b3a2703624f3d63bc8..ff3ba5422e9bdae64795eaa4fa617187f0b65725 100755 (executable)
@@ -155,9 +155,9 @@ test_expect_success 'submodule update --init --recursive from subdirectory' '
         cd tmp &&
         git submodule update --init --recursive ../super >../../actual 2>../../actual2
        ) &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
        sort actual2 >actual2.sorted &&
-       test_i18ncmp expect2 actual2.sorted
+       test_cmp expect2 actual2.sorted
 '
 
 cat <<EOF >expect2
@@ -174,7 +174,7 @@ test_expect_success 'submodule update --init from and of subdirectory' '
          git submodule update --init sub 2>../../actual2
         )
        ) &&
-       test_i18ncmp expect2 actual2
+       test_cmp expect2 actual2
 '
 
 test_expect_success 'submodule update does not fetch already present commits' '
@@ -192,7 +192,7 @@ test_expect_success 'submodule update does not fetch already present commits' '
        (cd super &&
          git submodule update > ../actual 2> ../actual.err
        ) &&
-       test_i18ncmp expected actual &&
+       test_cmp expected actual &&
        test_must_be_empty actual.err
 '
 
@@ -461,7 +461,7 @@ test_expect_success 'submodule update - command in .git/config catches failure'
        (cd super &&
         test_must_fail git submodule update submodule 2>../actual
        ) &&
-       test_i18ncmp actual expect
+       test_cmp actual expect
 '
 
 cat << EOF >expect
@@ -479,7 +479,7 @@ test_expect_success 'submodule update - command in .git/config catches failure -
         mkdir tmp && cd tmp &&
         test_must_fail git submodule update ../submodule 2>../../actual
        ) &&
-       test_i18ncmp actual expect
+       test_cmp actual expect
 '
 
 test_expect_success 'submodule update - command run for initial population of submodule' '
@@ -488,7 +488,7 @@ test_expect_success 'submodule update - command run for initial population of su
        EOF
        rm -rf super/submodule &&
        test_must_fail git -C super submodule update 2>actual &&
-       test_i18ncmp expect actual &&
+       test_cmp expect actual &&
        git -C super submodule update --checkout
 '
 
@@ -509,7 +509,7 @@ test_expect_success 'recursive submodule update - command in .git/config catches
         mkdir -p tmp && cd tmp &&
         test_must_fail git submodule update --recursive ../super 2>../../actual
        ) &&
-       test_i18ncmp actual expect
+       test_cmp actual expect
 '
 
 test_expect_success 'submodule init does not copy command into .git/config' '
index 79981b51eb89aced1aaaeee9004bfeb6592af000..e2f110b786387035f6cc4fa31a21f9c380edb2f4 100755 (executable)
@@ -80,7 +80,7 @@ test_expect_success 'test basic "submodule foreach" usage' '
                git config foo.bar zar &&
                git submodule foreach "git config --file \"\$toplevel/.git/config\" foo.bar"
        ) &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat >expect <<EOF
@@ -96,7 +96,7 @@ test_expect_success 'test "submodule foreach" from subdirectory' '
                cd clone/sub &&
                git submodule foreach "echo \$toplevel-\$name-\$sm_path-\$displaypath-\$sha1" >../../actual
        ) &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'setup nested submodules' '
@@ -177,7 +177,7 @@ test_expect_success 'test messages from "foreach --recursive"' '
                cd clone2 &&
                git submodule foreach --recursive "true" > ../actual
        ) &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat > expect <<EOF
@@ -197,7 +197,7 @@ test_expect_success 'test messages from "foreach --recursive" from subdirectory'
                cd untracked &&
                git submodule foreach --recursive >../../actual
        ) &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 sub1sha1=$(cd clone2/sub1 && git rev-parse HEAD)
 sub2sha1=$(cd clone2/sub2 && git rev-parse HEAD)
@@ -229,7 +229,7 @@ test_expect_success 'test "submodule foreach --recursive" from subdirectory' '
                cd clone2/untracked &&
                git submodule foreach --recursive "echo toplevel: \$toplevel name: \$name path: \$sm_path displaypath: \$displaypath hash: \$sha1" >../../actual
        ) &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 cat > expect <<EOF
index 6d19ece05dd320b970492daf015b874eee0d391f..e41ac18e7e0a663b44ef1cce0596f96615a7fc22 100755 (executable)
@@ -15,7 +15,7 @@ commit_msg_is () {
 
        printf "%s" "$(git log --pretty=format:%s%b -1)" >"$actual" &&
        printf "%s" "$1" >"$expect" &&
-       test_i18ncmp "$expect" "$actual"
+       test_cmp "$expect" "$actual"
 }
 
 # A sanity check to see if commit is working at all.
@@ -356,7 +356,7 @@ test_expect_success 'new line found before status message in commit template' '
        touch commit-template-check &&
        git add commit-template-check &&
        GIT_EDITOR="cat >editor-input" git commit --untracked-files=no --allow-empty-message &&
-       test_i18ncmp expected-template editor-input
+       test_cmp expected-template editor-input
 '
 
 test_expect_success 'setup empty commit with unstaged rename and copy' '
index 0f936182e4f186e98897ba4b8a11c5133ffc1911..512ae2781fe2c9b02a5a37f5230ec14992114ae7 100755 (executable)
@@ -11,7 +11,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY/diff-lib.sh"
+. "$TEST_DIRECTORY/lib-diff.sh"
 
 author='The Real Author <someguy@his.email.org>'
 
index e5332adc9a10aae26a2419d209d7337de4d5cae6..6396897cc81804be174db6e37ccc7a8af8d648f1 100755 (executable)
@@ -13,7 +13,7 @@ commit_msg_is () {
 
        printf "%s" "$(git log --pretty=format:%s%b -1)" >$actual &&
        printf "%s" "$1" >$expect &&
-       test_i18ncmp $expect $actual
+       test_cmp $expect $actual
 }
 
 # Arguments: [<prefix] [<commit message>] [<commit options>]
@@ -35,7 +35,7 @@ check_summary_oneline() {
        SUMMARY_POSTFIX="$(git log -1 --pretty='format:%h')"
        echo "[$SUMMARY_PREFIX $SUMMARY_POSTFIX] $2" >exp &&
 
-       test_i18ncmp exp act
+       test_cmp exp act
 }
 
 test_expect_success 'output summary format' '
@@ -300,7 +300,7 @@ echo "sample
 # with '#' will be ignored, and an empty message aborts the commit." >expect
 
 test_expect_success 'cleanup commit messages (strip option,-F,-e): output' '
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'cleanup commit message (fail on invalid cleanup mode option)' '
index 321b4bc0fc69d93a8db838447c01db76e0853c26..7a8194ce720ed5d9244caf2bbd882a56f491476c 100755 (executable)
@@ -218,7 +218,7 @@ test_expect_success 'with hook and editor (merge)' '
 test_rebase () {
        expect=$1 &&
        mode=$2 &&
-       test_expect_$expect C_LOCALE_OUTPUT "with hook (rebase ${mode:--i})" '
+       test_expect_$expect "with hook (rebase ${mode:--i})" '
                test_when_finished "\
                        git rebase --abort
                        git checkout -f main
@@ -307,7 +307,7 @@ test_expect_success 'with failing hook (merge)' '
 
 '
 
-test_expect_success C_LOCALE_OUTPUT 'with failing hook (cherry-pick)' '
+test_expect_success 'with failing hook (cherry-pick)' '
        test_when_finished "git checkout -f main" &&
        git checkout -B other b &&
        test_must_fail git cherry-pick rebase-1 2>actual &&
index d01aacb66b54e1700209e2338a39e062da7d8c36..2b72451ba3ed23eff7d726564845ce14f98e2cc1 100755 (executable)
@@ -109,13 +109,13 @@ test_expect_success 'status --column' '
 #
 EOF
        COLUMNS=50 git -c status.displayCommentPrefix=true status --column="column dense" >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status --column status.displayCommentPrefix=false' '
        strip_comments expect &&
        COLUMNS=49 git -c status.displayCommentPrefix=false status --column="column dense" >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect <<\EOF
@@ -144,19 +144,19 @@ EOF
 
 test_expect_success 'status with status.displayCommentPrefix=true' '
        git -c status.displayCommentPrefix=true status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status with status.displayCommentPrefix=false' '
        strip_comments expect &&
        git -c status.displayCommentPrefix=false status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status -v' '
        (cat expect && git diff --cached) >expect-with-v &&
        git status -v >output &&
-       test_i18ncmp expect-with-v output
+       test_cmp expect-with-v output
 '
 
 test_expect_success 'status -v -v' '
@@ -167,7 +167,7 @@ test_expect_success 'status -v -v' '
         echo "Changes not staged for commit:" &&
         git -c diff.mnemonicprefix=true diff) >expect-with-v &&
        git status -v -v >output &&
-       test_i18ncmp expect-with-v output
+       test_cmp expect-with-v output
 '
 
 test_expect_success 'setup fake editor' '
@@ -214,7 +214,7 @@ EOF
 test_expect_success 'status (advice.statusHints false)' '
        test_config advice.statusHints false &&
        git status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 
 '
 
@@ -296,7 +296,7 @@ Ignored files:
 
 EOF
        git status --ignored >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status with gitignore (nothing untracked)' '
@@ -358,7 +358,7 @@ Ignored files:
 
 EOF
        git status --ignored >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >.gitignore <<\EOF
@@ -380,7 +380,7 @@ EOF
 test_expect_success 'status -s -b' '
 
        git status -s -b >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 
 '
 
@@ -390,7 +390,7 @@ test_expect_success 'status -s -z -b' '
        git status -s -z -b >output &&
        nul_to_q <output >output.q &&
        mv output.q output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'setup dir3' '
@@ -418,13 +418,13 @@ Changes not staged for commit:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status -uno >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles no)' '
        test_config status.showuntrackedfiles no &&
        git status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status -uno (advice.statusHints false)' '
@@ -443,7 +443,7 @@ Untracked files not listed
 EOF
        test_config advice.statusHints false &&
        git status -uno >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect << EOF
@@ -487,13 +487,13 @@ Untracked files:
 
 EOF
        git status -unormal >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles normal)' '
        test_config status.showuntrackedfiles normal &&
        git status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -543,13 +543,13 @@ Untracked files:
 
 EOF
        git status -uall >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status (status.showUntrackedFiles all)' '
        test_config status.showuntrackedfiles all &&
        git status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'teardown dir3' '
@@ -601,7 +601,7 @@ Untracked files:
 
 EOF
        (cd dir1 && git status) >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect <<\EOF
@@ -670,13 +670,13 @@ Untracked files:
 EOF
        test_config color.ui auto &&
        test_terminal git status | test_decode_color >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success TTY 'status with color.status' '
        test_config color.status auto &&
        test_terminal git status | test_decode_color >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect <<\EOF
@@ -718,7 +718,7 @@ EOF
 test_expect_success TTY 'status -s -b with color.status' '
 
        test_terminal git status -s -b | test_decode_color >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 
 '
 
@@ -793,7 +793,7 @@ Untracked files:
 EOF
        test_config status.relativePaths false &&
        (cd dir1 && git status) >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 
 '
 
@@ -860,7 +860,7 @@ Untracked files:
 
 EOF
        git commit --dry-run dir1/modified >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -921,13 +921,13 @@ Untracked files:
 
 EOF
        git status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 # we expect the same as the previous test
 test_expect_success 'status --untracked-files=all does not show submodule' '
        git status --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -984,13 +984,13 @@ Untracked files:
 EOF
        git config status.submodulesummary 10 &&
        git status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'status submodule summary with status.displayCommentPrefix=false' '
        strip_comments expect &&
        git -c status.displayCommentPrefix=false status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'commit with submodule summary ignores status.displayCommentPrefix' '
@@ -1035,9 +1035,9 @@ EOF
        git commit -m "commit submodule" &&
        git config status.submodulesummary 10 &&
        test_must_fail git commit --dry-run >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 cat >expect <<EOF
@@ -1091,7 +1091,7 @@ Untracked files:
 EOF
        git config status.submodulesummary 10 &&
        git commit --dry-run --amend >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' '
@@ -1143,17 +1143,17 @@ Untracked files:
 EOF
        echo modified  sm/untracked &&
        git status --ignore-submodules=untracked >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' '
        test_config diff.ignoreSubmodules dirty &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --add -f .gitmodules submodule.subname.ignore untracked &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1163,14 +1163,14 @@ test_expect_success '.git/config ignore=untracked suppresses submodules with unt
        git config --add submodule.subname.ignore untracked &&
        git config --add submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --remove-section submodule.subname &&
        git config --remove-section -f .gitmodules submodule.subname
 '
 
 test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' '
        git status --ignore-submodules=dirty >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' '
@@ -1180,7 +1180,7 @@ test_expect_success '.gitmodules ignore=dirty suppresses submodules with untrack
        git config --add -f .gitmodules submodule.subname.ignore dirty &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1190,7 +1190,7 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with untrack
        git config --add submodule.subname.ignore dirty &&
        git config --add submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --remove-section submodule.subname &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1198,14 +1198,14 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with untrack
 test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' '
        echo modified >sm/foo &&
        git status --ignore-submodules=dirty >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' '
        git config --add -f .gitmodules submodule.subname.ignore dirty &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1215,7 +1215,7 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with modifie
        git config --add submodule.subname.ignore dirty &&
        git config --add submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --remove-section submodule.subname &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1253,14 +1253,14 @@ Untracked files:
 
 EOF
        git status --ignore-submodules=untracked > output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" '
        git config --add -f .gitmodules submodule.subname.ignore untracked &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1270,7 +1270,7 @@ test_expect_success ".git/config ignore=untracked doesn't suppress submodules wi
        git config --add submodule.subname.ignore untracked &&
        git config --add submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --remove-section submodule.subname &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1314,14 +1314,14 @@ Untracked files:
 
 EOF
        git status --ignore-submodules=untracked > output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" '
        git config --add -f .gitmodules submodule.subname.ignore untracked &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1331,20 +1331,20 @@ test_expect_success ".git/config ignore=untracked doesn't suppress submodule sum
        git config --add submodule.subname.ignore untracked &&
        git config --add submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --remove-section submodule.subname &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
 test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" '
        git status --ignore-submodules=dirty > output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" '
        git config --add -f .gitmodules submodule.subname.ignore dirty &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1354,7 +1354,7 @@ test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary
        git config --add submodule.subname.ignore dirty &&
        git config --add submodule.subname.path sm &&
        git status >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --remove-section submodule.subname &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1398,7 +1398,7 @@ EOF
 test_expect_success "status (core.commentchar with submodule summary)" '
        test_config core.commentchar ";" &&
        git -c status.displayCommentPrefix=true status >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success "status (core.commentchar with two chars with submodule summary)" '
@@ -1429,7 +1429,7 @@ Untracked files:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --ignore-submodules=all > output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' '
@@ -1460,7 +1460,7 @@ EOF
        git config --add -f .gitmodules submodule.subname.ignore all &&
        git config --add -f .gitmodules submodule.subname.path sm &&
        git status > output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
 
@@ -1470,7 +1470,7 @@ test_expect_success '.git/config ignore=all suppresses unstaged submodule summar
        git config --add submodule.subname.ignore all &&
        git config --add submodule.subname.path sm &&
        git status > output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git config --remove-section submodule.subname &&
        git config -f .gitmodules  --remove-section submodule.subname
 '
@@ -1571,7 +1571,7 @@ Changes not staged for commit:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git commit -uno --dry-run >output &&
-       test_i18ncmp expect output &&
+       test_cmp expect output &&
        git status -s --ignore-submodules=dirty >output &&
        test_i18ngrep "^M. sm" output
 '
index f4bf925bdd26bbbe062c268536054ce8019253ba..8df5a74f1db4ecd846c9365c0474f6628cbbd443 100755 (executable)
@@ -175,7 +175,7 @@ test_expect_success GPG 'show signed commit with signature' '
        git cat-file commit initial >cat &&
        grep -v -e "gpg: " -e "Warning: " show >show.commit &&
        grep -e "gpg: " -e "Warning: " show >show.gpg &&
-       grep -v "^ " cat | grep -v "^$(test_oid header) " >cat.commit &&
+       grep -v "^ " cat | grep -v "^gpgsig.* " >cat.commit &&
        test_cmp show.commit commit &&
        test_cmp show.gpg verify.2 &&
        test_cmp cat.commit verify.1
@@ -337,4 +337,45 @@ test_expect_success GPG 'show double signature with custom format' '
        test_cmp expect actual
 '
 
+
+test_expect_success GPG 'verify-commit verifies multiply signed commits' '
+       git init multiply-signed &&
+       cd multiply-signed &&
+       test_commit first &&
+       echo 1 >second &&
+       git add second &&
+       tree=$(git write-tree) &&
+       parent=$(git rev-parse HEAD^{commit}) &&
+       git commit --gpg-sign -m second &&
+       git cat-file commit HEAD &&
+       # Avoid trailing whitespace.
+       sed -e "s/^Q//" -e "s/^Z/ /" >commit <<-EOF &&
+       Qtree $tree
+       Qparent $parent
+       Qauthor A U Thor <author@example.com> 1112912653 -0700
+       Qcommitter C O Mitter <committer@example.com> 1112912653 -0700
+       Qgpgsig -----BEGIN PGP SIGNATURE-----
+       QZ
+       Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBDRYcY29tbWl0dGVy
+       Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMNd+8AoK1I8mhLHviPH+q2I5fIVgPsEtYC
+       Q AKCTqBh+VabJceXcGIZuF0Ry+udbBQ==
+       Q =tQ0N
+       Q -----END PGP SIGNATURE-----
+       Qgpgsig-sha256 -----BEGIN PGP SIGNATURE-----
+       QZ
+       Q iHQEABECADQWIQRz11h0S+chaY7FTocTtvUezd5DDQUCX/uBIBYcY29tbWl0dGVy
+       Q QGV4YW1wbGUuY29tAAoJEBO29R7N3kMN/NEAn0XO9RYSBj2dFyozi0JKSbssYMtO
+       Q AJwKCQ1BQOtuwz//IjU8TiS+6S4iUw==
+       Q =pIwP
+       Q -----END PGP SIGNATURE-----
+       Q
+       Qsecond
+       EOF
+       head=$(git hash-object -t commit -w commit) &&
+       git reset --hard $head &&
+       git verify-commit $head 2>actual &&
+       grep "Good signature from" actual &&
+       ! grep "BAD signature from" actual
+'
+
 test_done
index 9f5e3ce7931991d29d9108b8f8522c1eba6e3bc6..7f2956d77ad033a57981a9569db3ef803ef93961 100755 (executable)
@@ -41,7 +41,7 @@ Unmerged paths:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -61,7 +61,7 @@ Changes to be committed:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -93,7 +93,7 @@ Unmerged paths:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -116,7 +116,7 @@ Changes to be committed:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -154,7 +154,7 @@ Unmerged paths:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -180,7 +180,7 @@ Changes to be committed:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -210,7 +210,7 @@ You are currently editing a commit while rebasing branch '\''rebase_i_edit'\'' o
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -249,7 +249,7 @@ Changes not staged for commit:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -282,7 +282,7 @@ You are currently editing a commit while rebasing branch '\''amend_last'\'' on '
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -321,7 +321,7 @@ You are currently editing a commit while rebasing branch '\''several_edits'\'' o
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -356,7 +356,7 @@ Changes not staged for commit:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -387,7 +387,7 @@ You are currently editing a commit while rebasing branch '\''several_edits'\'' o
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -418,7 +418,7 @@ You are currently editing a commit while rebasing branch '\''several_edits'\'' o
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -454,7 +454,7 @@ Changes not staged for commit:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -486,7 +486,7 @@ You are currently editing a commit while rebasing branch '\''several_edits'\'' o
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -519,7 +519,7 @@ You are currently editing a commit while rebasing branch '\''several_edits'\'' o
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -557,7 +557,7 @@ Changes not staged for commit:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -591,7 +591,7 @@ You are currently editing a commit while rebasing branch '\''several_edits'\'' o
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -619,7 +619,7 @@ You are in the middle of an am session.
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -641,7 +641,7 @@ You are in the middle of an am session.
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -664,7 +664,7 @@ The current patch is empty.
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -687,7 +687,7 @@ You are currently bisecting, started from branch '\''bisect'\''.
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -712,7 +712,7 @@ Unmerged paths:
 no changes added to commit
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -746,7 +746,7 @@ Unmerged paths:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 
@@ -770,7 +770,7 @@ Changes to be committed:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status when cherry-picking after committing conflict resolution' '
@@ -789,7 +789,7 @@ Cherry-pick currently in progress.
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status shows cherry-pick with invalid oid' '
@@ -798,7 +798,7 @@ test_expect_success 'status shows cherry-pick with invalid oid' '
        git status --untracked-files=no >actual 2>err &&
        git cherry-pick --quit &&
        test_must_be_empty err &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status does not show error if .git/sequencer is a file' '
@@ -816,7 +816,7 @@ HEAD detached at atag
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual &&
+       test_cmp expected actual &&
 
        git reset --hard HEAD^ &&
        cat >expected <<\EOF &&
@@ -824,7 +824,7 @@ HEAD detached from atag
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status while reverting commit (conflicts)' '
@@ -852,7 +852,7 @@ Unmerged paths:
 no changes added to commit (use "git add" and/or "git commit -a")
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status while reverting commit (conflicts resolved)' '
@@ -872,7 +872,7 @@ Changes to be committed:
 Untracked files not listed (use -u option to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status after reverting commit' '
@@ -882,7 +882,7 @@ On branch main
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status while reverting after committing conflict resolution' '
@@ -901,7 +901,7 @@ Revert currently in progress.
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'prepare for different number of commits rebased' '
@@ -931,7 +931,7 @@ You are currently editing a commit while rebasing branch '\''several_commits'\''
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status: two commands done with some white lines in done file' '
@@ -959,7 +959,7 @@ You are currently editing a commit while rebasing branch '\''several_commits'\''
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status: two remaining commands with some white lines in todo file' '
@@ -988,7 +988,7 @@ You are currently editing a commit while rebasing branch '\''several_commits'\''
 nothing to commit (use -u to show untracked files)
 EOF
        git status --untracked-files=no >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'status: handle not-yet-started rebase -i gracefully' '
@@ -1007,7 +1007,7 @@ You are currently editing a commit while rebasing branch '\''several_commits'\''
 
 nothing to commit (use -u to show untracked files)
 EOF
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_done
index fbfdcca000777461aed96c19eb7c2cc4b298c86c..45d025f96010da185f4af33b02ee60004236b1ea 100755 (executable)
@@ -236,7 +236,7 @@ test_expect_success 'refresh_index() invalidates fsmonitor cache' '
        git reset HEAD~1 &&
        git status >actual &&
        git -c core.fsmonitor= status >expect &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 # test fsmonitor with and without preloadIndex
@@ -273,7 +273,7 @@ do
                        git add dir2/new &&
                        git status >actual &&
                        git -c core.fsmonitor= status >expect &&
-                       test_i18ncmp expect actual
+                       test_cmp expect actual
                '
 
                # Make sure it's actually skipping the check for modified and untracked
index 91790943c3df0555c1dbaadc05a0832f4961f9bf..a88b02b06ed34234abfb3aa3131dd31a7d364141 100755 (executable)
@@ -30,7 +30,7 @@ test_expect_success 'Verify behavior of status on directories with ignored files
                dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
        git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on directory with tracked & ignored files' '
@@ -55,7 +55,7 @@ test_expect_success 'Verify status behavior on directory with tracked & ignored
        git commit -m "commit tracked files" &&
 
        git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on directory with untracked and ignored files' '
@@ -80,7 +80,7 @@ test_expect_success 'Verify status behavior on directory with untracked and igno
                dir/untracked_ignored/ignored_1.ign dir/untracked_ignored/ignored_2.ign &&
 
        git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify status matching ignored files on ignored directory' '
@@ -96,7 +96,7 @@ test_expect_success 'Verify status matching ignored files on ignored directory'
                ignored_dir/ignored_1.ign ignored_dir/ignored_2.ign &&
 
        git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
@@ -117,7 +117,7 @@ test_expect_success 'Verify status behavior on ignored directory containing trac
        git add -f ignored_dir/tracked &&
        git commit -m "Force add file in ignored directory" &&
        git status --porcelain=v2 --ignored=matching --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
@@ -136,7 +136,7 @@ test_expect_success 'Verify matching ignored files with --untracked-files=normal
                ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
                untracked_dir/untracked &&
        git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify matching ignored files with --untracked-files=normal' '
@@ -155,7 +155,7 @@ test_expect_success 'Verify matching ignored files with --untracked-files=normal
                ignored_files/ignored_1.ign ignored_files/ignored_2.ign \
                untracked_dir/untracked &&
        git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify status behavior on ignored directory containing tracked file' '
@@ -176,7 +176,7 @@ test_expect_success 'Verify status behavior on ignored directory containing trac
        git add -f ignored_dir/tracked &&
        git commit -m "Force add file in ignored directory" &&
        git status --porcelain=v2 --ignored=matching --untracked-files=normal >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify behavior of status with --ignored=no' '
@@ -191,7 +191,7 @@ test_expect_success 'Verify behavior of status with --ignored=no' '
                dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
        git status --porcelain=v2 --ignored=no --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=all' '
@@ -210,7 +210,7 @@ test_expect_success 'Verify behavior of status with --ignored=traditional and --
                dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
        git status --porcelain=v2 --ignored=traditional --untracked-files=all >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_expect_success 'Verify behavior of status with --ignored=traditional and --untracked-files=normal' '
@@ -227,7 +227,7 @@ test_expect_success 'Verify behavior of status with --ignored=traditional and --
                dir/ignored/ignored_1.ign dir/ignored/ignored_2.ign &&
 
        git status --porcelain=v2 --ignored=traditional --untracked-files=normal >output &&
-       test_i18ncmp expect output
+       test_cmp expect output
 '
 
 test_done
index b2c1d861dcb07aa0dd87b8b88269d9e7cb5d69aa..1cbc9715a81bdc9cf13bbe6d1341388468d0d047 100755 (executable)
@@ -272,7 +272,7 @@ test_expect_success 'merge c3 with c7 with commit.cleanup = scissors' '
        EOF
        git cat-file commit HEAD >raw &&
        sed -e "1,/^$/d" raw >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '
@@ -296,7 +296,7 @@ test_expect_success 'merge c3 with c7 with --squash commit.cleanup = scissors' '
        EOF
        git cat-file commit HEAD >raw &&
        sed -e "1,/^$/d" raw >actual &&
-       test_i18ncmp expect actual
+       test_cmp expect actual
 '
 
 test_debug 'git log --graph --decorate --oneline --all'
index 13859ec8595f7f5abc32cc5322e83328a4556319..a9c816b47f269ad0bf073f97f50e03a6ba3890d7 100755 (executable)
@@ -66,7 +66,7 @@ EOF
 test_expect_success 'merge output uses pretty names' '
        git reset --hard c1 &&
        git merge c2 c3 c4 >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 cat >expected <<\EOF
@@ -84,7 +84,7 @@ test_expect_success 'merge reduces irrelevant remote heads' '
                rm expected.tmp
        fi &&
        GIT_MERGE_VERBOSITY=0 git merge c4 c5 >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 cat >expected <<\EOF
@@ -101,7 +101,7 @@ EOF
 test_expect_success 'merge fast-forward output uses pretty names' '
        git reset --hard c0 &&
        git merge c1 c2 >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_done
index 04b0095072d2ce1b347d1cd48e81d0be5c182dc2..8cc64729adb20640f17de4aa9ba891a9ead7edde 100755 (executable)
@@ -842,4 +842,22 @@ test_expect_success 'mergetool --tool-help shows recognized tools' '
        grep meld mergetools
 '
 
+test_expect_success 'mergetool hideResolved' '
+       test_config mergetool.hideResolved true &&
+       test_when_finished "git reset --hard" &&
+       git checkout -b test${test_count}_b main &&
+       test_write_lines >file1 base "" a &&
+       git commit -a -m "base" &&
+       test_write_lines >file1 base "" c &&
+       git commit -a -m "remote update" &&
+       git checkout -b test${test_count}_a HEAD~ &&
+       test_write_lines >file1 local "" b &&
+       git commit -a -m "local update" &&
+       test_must_fail git merge test${test_count}_b &&
+       yes "" | git mergetool file1 &&
+       test_write_lines >expect local "" c &&
+       test_cmp expect file1 &&
+       git commit -m "test resolved with mergetool"
+'
+
 test_done
index 8f7591c9ccdac33a46b8db0caa59030c670e8d79..edfaa9a6d1cbe962c5b3e138adc529a5df3397e7 100755 (executable)
@@ -969,7 +969,7 @@ do
        "
 done
 
-test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'grep --threads=N or pack.threads=N warns when no pthreads' '
+test_expect_success !PTHREADS 'grep --threads=N or pack.threads=N warns when no pthreads' '
        git grep --threads=2 Hello hello_world 2>err &&
        grep ^warning: err >warnings &&
        test_line_count = 1 warnings &&
index 78ccf4b33f87c98d8b795ea7e31b6c29fc8837fb..286b18db3cc2d59e000f897d4a9fa3ad7ae46af7 100755 (executable)
@@ -343,6 +343,18 @@ test_expect_success 'maintenance.incremental-repack.auto' '
        test_subcommand git multi-pack-index write --no-progress <trace-B
 '
 
+test_expect_success 'pack-refs task' '
+       for n in $(test_seq 1 5)
+       do
+               git branch -f to-pack/$n HEAD || return 1
+       done &&
+       GIT_TRACE2_EVENT="$(pwd)/pack-refs.txt" \
+               git maintenance run --task=pack-refs &&
+       ls .git/refs/heads/ >after &&
+       test_must_be_empty after &&
+       test_subcommand git pack-refs --all --prune <pack-refs.txt
+'
+
 test_expect_success '--auto and --schedule incompatible' '
        test_must_fail git maintenance run --auto --schedule=daily 2>err &&
        test_i18ngrep "at most one" err
@@ -396,18 +408,32 @@ test_expect_success 'maintenance.strategy inheritance' '
                git maintenance run --schedule=hourly --quiet &&
        GIT_TRACE2_EVENT="$(pwd)/incremental-daily.txt" \
                git maintenance run --schedule=daily --quiet &&
+       GIT_TRACE2_EVENT="$(pwd)/incremental-weekly.txt" \
+               git maintenance run --schedule=weekly --quiet &&
 
        test_subcommand git commit-graph write --split --reachable \
                --no-progress <incremental-hourly.txt &&
        test_subcommand ! git prune-packed --quiet <incremental-hourly.txt &&
        test_subcommand ! git multi-pack-index write --no-progress \
                <incremental-hourly.txt &&
+       test_subcommand ! git pack-refs --all --prune \
+               <incremental-hourly.txt &&
 
        test_subcommand git commit-graph write --split --reachable \
                --no-progress <incremental-daily.txt &&
        test_subcommand git prune-packed --quiet <incremental-daily.txt &&
        test_subcommand git multi-pack-index write --no-progress \
                <incremental-daily.txt &&
+       test_subcommand ! git pack-refs --all --prune \
+               <incremental-daily.txt &&
+
+       test_subcommand git commit-graph write --split --reachable \
+               --no-progress <incremental-weekly.txt &&
+       test_subcommand git prune-packed --quiet <incremental-weekly.txt &&
+       test_subcommand git multi-pack-index write --no-progress \
+               <incremental-weekly.txt &&
+       test_subcommand git pack-refs --all --prune \
+               <incremental-weekly.txt &&
 
        # Modify defaults
        git config maintenance.commit-graph.schedule daily &&
index 03cd5c5423627fa7c5162fb84ad41cdb1960a645..f00deaf3815f3b49c42da4021335e011bbcb3c32 100755 (executable)
@@ -56,11 +56,8 @@ test_expect_success 'autocorrect can be declined altogether' '
        git config help.autocorrect never &&
 
        test_must_fail git lfg 2>actual &&
-       if test_have_prereq C_LOCALE_OUTPUT
-       then
-               grep "is not a git command" actual &&
-               test_line_count = 1 actual
-       fi
+       grep "is not a git command" actual &&
+       test_line_count = 1 actual
 '
 
 test_done
index 696ace2462559eb8bfa336db762923c449914db2..1fbe84feb1689cc413725f49189bcdd7f1379139 100755 (executable)
@@ -12,49 +12,46 @@ export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 test_expect_success 'load svn dump' "
        svnadmin load -q '$rawsvnrepo' \
-         < '$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' &&
+         <'$TEST_DIRECTORY/t9151/svn-mergeinfo.dump' &&
        git svn init --minimize-url -R svnmerge \
          --rewrite-root=http://svn.example.org \
          -T trunk -b branches '$svnrepo' &&
        git svn fetch --all
-       "
+"
 
 test_expect_success 'all svn merges became git merge commits' '
-       unmarked=$(git rev-list --parents --all --grep=Merge |
-               grep -v " .* " | cut -f1 -d" ") &&
-       [ -z "$unmarked" ]
-       '
+       git rev-list --all --no-merges --grep=Merge >unmarked &&
+       test_must_be_empty unmarked
+'
 
 test_expect_success 'cherry picks did not become git merge commits' '
-       bad_cherries=$(git rev-list --parents --all --grep=Cherry |
-               grep " .* " | cut -f1 -d" ") &&
-       [ -z "$bad_cherries" ]
-       '
+       git rev-list --all --merges --grep=Cherry >bad-cherries &&
+       test_must_be_empty bad-cherries
+'
 
 test_expect_success 'svn non-merge merge commits did not become git merge commits' '
-       bad_non_merges=$(git rev-list --parents --all --grep=non-merge |
-               grep " .* " | cut -f1 -d" ") &&
-       [ -z "$bad_non_merges" ]
-       '
+       git rev-list --all --merges --grep=non-merge >bad-non-merges &&
+       test_must_be_empty bad-non-merges
+'
 
 test_expect_success 'commit made to merged branch is reachable from the merge' '
        before_commit=$(git rev-list --all --grep="trunk commit before merging trunk to b2") &&
        merge_commit=$(git rev-list --all --grep="Merge trunk to b2") &&
-       not_reachable=$(git rev-list -1 $before_commit --not $merge_commit) &&
-       [ -z "$not_reachable" ]
-       '
+       git rev-list -1 $before_commit --not $merge_commit >not-reachable &&
+       test_must_be_empty not-reachable
+'
 
 test_expect_success 'merging two branches in one commit is detected correctly' '
        f1_commit=$(git rev-list --all --grep="make f1 branch from trunk") &&
        f2_commit=$(git rev-list --all --grep="make f2 branch from trunk") &&
        merge_commit=$(git rev-list --all --grep="Merge f1 and f2 to trunk") &&
-       not_reachable=$(git rev-list -1 $f1_commit $f2_commit --not $merge_commit) &&
-       [ -z "$not_reachable" ]
-       '
+       git rev-list -1 $f1_commit $f2_commit --not $merge_commit >not-reachable &&
+       test_must_be_empty not-reachable
+'
 
 test_expect_failure 'everything got merged in the end' '
-       unmerged=$(git rev-list --all --not main) &&
-       [ -z "$unmerged" ]
-       '
+       git rev-list --all --not main >unmerged &&
+       test_must_be_empty unmerged
+'
 
 test_done
index 3d17e932a0e3fa9958146546e0aa325b7122600f..5c47ac4465cb554fbfc9bf48e674f41f5e0ac041 100755 (executable)
@@ -8,7 +8,7 @@ GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
 . ./test-lib.sh
-. "$TEST_DIRECTORY"/diff-lib.sh ;# test-lib chdir's into trash
+. "$TEST_DIRECTORY"/lib-diff.sh ;# test-lib chdir's into trash
 
 verify_packs () {
        for p in .git/objects/pack/*.pack
@@ -1632,7 +1632,10 @@ test_expect_success 'O: blank lines not necessary after other commands' '
        INPUT_END
 
        git fast-import <input &&
-       test 8 = $(find .git/objects/pack -type f | grep -v multi-pack-index | wc -l) &&
+       ls -la .git/objects/pack/pack-*.pack >packlist &&
+       ls -la .git/objects/pack/pack-*.pack >idxlist &&
+       test_line_count = 4 idxlist &&
+       test_line_count = 4 packlist &&
        test $(git rev-parse refs/tags/O3-2nd) = $(git rev-parse O3^) &&
        git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
        test_cmp expect actual
index ee8c6e30e67f5b969bd32889f3f57a1e11771e90..0333065d4d60a0113cb5ac1de391a4a997d2eaa8 100755 (executable)
@@ -13,7 +13,7 @@ or warnings to log.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
-. ./gitweb-lib.sh
+. ./lib-gitweb.sh
 
 # ----------------------------------------------------------------------
 # no commits (empty, just initialized repository)
index 141610de5464882eb660133d80ceb65df7323b17..32814e75df5b0d796b292c2ac39d370c636e5e67 100755 (executable)
@@ -13,7 +13,7 @@ code and message.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
-. ./gitweb-lib.sh
+. ./lib-gitweb.sh
 
 #
 # Gitweb only provides the functionality tested by the 'modification times'
index 9cf7ab30a8d8f824088a02ddd646fb7a6a881378..3167473b3031c66555883745d296a881593b8272 100755 (executable)
@@ -13,7 +13,7 @@ in the HTTP header or the actual script output.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
-. ./gitweb-lib.sh
+. ./lib-gitweb.sh
 
 # ----------------------------------------------------------------------
 # snapshot file name and prefix
index 07976af81c81120af0c1aaf9e39f6c3b0f33a2bf..6348e8d7339cda585d9bd20ca867d6638e706364 100644 (file)
@@ -32,11 +32,6 @@ test_set_editor () {
        export EDITOR
 }
 
-test_set_index_version () {
-    GIT_INDEX_VERSION="$1"
-    export GIT_INDEX_VERSION
-}
-
 test_decode_color () {
        awk '
                function name(n) {
@@ -116,13 +111,6 @@ remove_cr () {
        tr '\015' Q | sed -e 's/Q$//'
 }
 
-# Generate an output of $1 bytes of all zeroes (NULs, not ASCII zeroes).
-# If $1 is 'infinity', output forever or until the receiving pipe stops reading,
-# whichever comes first.
-generate_zero_bytes () {
-       test-tool genzeros "$@"
-}
-
 # In some bourne shell implementations, the "unset" builtin returns
 # nonzero status when a variable to be unset was not set in the first
 # place.
@@ -202,6 +190,7 @@ test_commit () {
        author= &&
        signoff= &&
        indir= &&
+       no_tag= &&
        while test $# != 0
        do
                case "$1" in
@@ -218,10 +207,19 @@ test_commit () {
                --signoff)
                        signoff="$1"
                        ;;
+               --date)
+                       notick=yes
+                       GIT_COMMITTER_DATE="$2"
+                       GIT_AUTHOR_DATE="$2"
+                       shift
+                       ;;
                -C)
                        indir="$2"
                        shift
                        ;;
+               --no-tag)
+                       no_tag=yes
+                       ;;
                *)
                        break
                        ;;
@@ -244,7 +242,10 @@ test_commit () {
        git ${indir:+ -C "$indir"} commit \
            ${author:+ --author "$author"} \
            $signoff -m "$1" &&
-       git ${indir:+ -C "$indir"} tag "${4:-$1}"
+       if test -z "$no_tag"
+       then
+               git ${indir:+ -C "$indir"} tag "${4:-$1}"
+       fi
 }
 
 # Call test_merge with the arguments "<message> <commit>", where <commit>
@@ -729,34 +730,37 @@ test_external_without_stderr () {
 }
 
 # debugging-friendly alternatives to "test [-f|-d|-e]"
-# The commands test the existence or non-existence of $1. $2 can be
-# given to provide a more precise diagnosis.
+# The commands test the existence or non-existence of $1
 test_path_is_file () {
+       test "$#" -ne 1 && BUG "1 param"
        if ! test -f "$1"
        then
-               echo "File $1 doesn't exist. $2"
+               echo "File $1 doesn't exist"
                false
        fi
 }
 
 test_path_is_dir () {
+       test "$#" -ne 1 && BUG "1 param"
        if ! test -d "$1"
        then
-               echo "Directory $1 doesn't exist. $2"
+               echo "Directory $1 doesn't exist"
                false
        fi
 }
 
 test_path_exists () {
+       test "$#" -ne 1 && BUG "1 param"
        if ! test -e "$1"
        then
-               echo "Path $1 doesn't exist. $2"
+               echo "Path $1 doesn't exist"
                false
        fi
 }
 
 # Check if the directory exists and is empty as expected, barf otherwise.
 test_dir_is_empty () {
+       test "$#" -ne 1 && BUG "1 param"
        test_path_is_dir "$1" &&
        if test -n "$(ls -a1 "$1" | egrep -v '^\.\.?$')"
        then
@@ -768,6 +772,7 @@ test_dir_is_empty () {
 
 # Check if the file exists and has a size greater than zero
 test_file_not_empty () {
+       test "$#" = 2 && BUG "2 param"
        if ! test -s "$1"
        then
                echo "'$1' is not a non-empty file."
@@ -776,6 +781,7 @@ test_file_not_empty () {
 }
 
 test_path_is_missing () {
+       test "$#" -ne 1 && BUG "1 param"
        if test -e "$1"
        then
                echo "Path exists:"
@@ -812,6 +818,7 @@ test_line_count () {
 }
 
 test_file_size () {
+       test "$#" -ne 1 && BUG "1 param"
        test-tool path-utils file-size "$1"
 }
 
@@ -984,6 +991,7 @@ test_expect_code () {
 # - not all diff versions understand "-u"
 
 test_cmp () {
+       test "$#" -ne 2 && BUG "2 param"
        eval "$GIT_TEST_CMP" '"$@"'
 }
 
@@ -1013,6 +1021,7 @@ test_cmp_config () {
 # test_cmp_bin - helper to compare binary files
 
 test_cmp_bin () {
+       test "$#" -ne 2 && BUG "2 param"
        cmp "$@"
 }
 
@@ -1073,6 +1082,7 @@ verbose () {
 # otherwise.
 
 test_must_be_empty () {
+       test "$#" -ne 1 && BUG "1 param"
        test_path_is_file "$1" &&
        if test -s "$1"
        then
@@ -1096,7 +1106,7 @@ test_cmp_rev () {
        fi
        if test $# != 2
        then
-               error "bug in the test script: test_cmp_rev requires two revisions, but got $#"
+               BUG "test_cmp_rev requires two revisions, but got $#"
        else
                local r1 r2
                r1=$(git rev-parse --verify "$1") &&
@@ -1207,7 +1217,7 @@ test_atexit () {
        # doing so on Bash is better than nothing (the test will
        # silently pass on other shells).
        test "${BASH_SUBSHELL-0}" = 0 ||
-       error "bug in test script: test_atexit does nothing in a subshell"
+       BUG "test_atexit does nothing in a subshell"
        test_atexit_cleanup="{ $*
                } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_atexit_cleanup"
 }
@@ -1605,33 +1615,6 @@ test_set_port () {
        eval $var=$port
 }
 
-# Compare a file containing rev-list bitmap traversal output to its non-bitmap
-# counterpart. You can't just use test_cmp for this, because the two produce
-# subtly different output:
-#
-#   - regular output is in traversal order, whereas bitmap is split by type,
-#     with non-packed objects at the end
-#
-#   - regular output has a space and the pathname appended to non-commit
-#     objects; bitmap output omits this
-#
-# This function normalizes and compares the two. The second file should
-# always be the bitmap output.
-test_bitmap_traversal () {
-       if test "$1" = "--no-confirm-bitmaps"
-       then
-               shift
-       elif cmp "$1" "$2"
-       then
-               echo >&2 "identical raw outputs; are you sure bitmaps were used?"
-               return 1
-       fi &&
-       cut -d' ' -f1 "$1" | sort >"$1.normalized" &&
-       sort "$2" >"$2.normalized" &&
-       test_cmp "$1.normalized" "$2.normalized" &&
-       rm -f "$1.normalized" "$2.normalized"
-}
-
 # Tests for the hidden file attribute on Windows
 test_path_is_hidden () {
        test_have_prereq MINGW ||
index 431adba0fb3f8eafbf28795c15b8a8c941b51f9e..d3f6af6a65451cdd7868e5dc355b44fed49a0670 100644 (file)
@@ -453,36 +453,6 @@ export GIT_DEFAULT_HASH
 GIT_TRACE_BARE=1
 export GIT_TRACE_BARE
 
-check_var_migration () {
-       # the warnings and hints given from this helper depends
-       # on end-user settings, which will disrupt the self-test
-       # done on the test framework itself.
-       case "$GIT_TEST_FRAMEWORK_SELFTEST" in
-       t)      return ;;
-       esac
-
-       old_name=$1 new_name=$2
-       eval "old_isset=\${${old_name}:+isset}"
-       eval "new_isset=\${${new_name}:+isset}"
-
-       case "$old_isset,$new_isset" in
-       isset,)
-               echo >&2 "warning: $old_name is now $new_name"
-               echo >&2 "hint: set $new_name too during the transition period"
-               eval "$new_name=\$$old_name"
-               ;;
-       isset,isset)
-               # do this later
-               # echo >&2 "warning: $old_name is now $new_name"
-               # echo >&2 "hint: remove $old_name"
-               ;;
-       esac
-}
-
-check_var_migration GIT_FSMONITOR_TEST GIT_TEST_FSMONITOR
-check_var_migration TEST_GIT_INDEX_VERSION GIT_TEST_INDEX_VERSION
-check_var_migration GIT_FORCE_PRELOAD_TEST GIT_TEST_PRELOAD_INDEX
-
 # Use specific version of the index file format
 if test -n "${GIT_TEST_INDEX_VERSION:+isset}"
 then
@@ -1519,11 +1489,6 @@ test -n "$USE_LIBPCRE2" && test_set_prereq PCRE
 test -n "$USE_LIBPCRE2" && test_set_prereq LIBPCRE2
 test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
 
-# Used to be used for GIT_TEST_GETTEXT_POISON=false. Only here as a
-# shim for other in-flight changes. Should not be used and will be
-# removed soon.
-test_set_prereq C_LOCALE_OUTPUT
-
 if test -z "$GIT_TEST_CHECK_CACHE_TREE"
 then
        GIT_TEST_CHECK_CACHE_TREE=true
diff --git a/tag.c b/tag.c
index 1ed2684e45bd50d051ade60cc6b66c8c44fbed59..3e18a41841485e8138d7ccaf36e24cc99e5d81cd 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -13,26 +13,27 @@ const char *tag_type = "tag";
 static int run_gpg_verify(const char *buf, unsigned long size, unsigned flags)
 {
        struct signature_check sigc;
-       size_t payload_size;
+       struct strbuf payload = STRBUF_INIT;
+       struct strbuf signature = STRBUF_INIT;
        int ret;
 
        memset(&sigc, 0, sizeof(sigc));
 
-       payload_size = parse_signature(buf, size);
-
-       if (size == payload_size) {
+       if (!parse_signature(buf, size, &payload, &signature)) {
                if (flags & GPG_VERIFY_VERBOSE)
-                       write_in_full(1, buf, payload_size);
+                       write_in_full(1, buf, size);
                return error("no signature found");
        }
 
-       ret = check_signature(buf, payload_size, buf + payload_size,
-                               size - payload_size, &sigc);
+       ret = check_signature(payload.buf, payload.len, signature.buf,
+                               signature.len, &sigc);
 
        if (!(flags & GPG_VERIFY_OMIT_STATUS))
                print_signature_buffer(&sigc, flags);
 
        signature_check_clear(&sigc);
+       strbuf_release(&payload);
+       strbuf_release(&signature);
        return ret;
 }
 
index 42ed4db5d3a54e1dbb2bd2fb56481da28411ca1f..b8d880e3626a9cdbc818cf69bda67fcd256194b1 100644 (file)
@@ -185,9 +185,11 @@ static int pack_copy_priority(const char *name)
                return 1;
        if (ends_with(name, ".pack"))
                return 2;
-       if (ends_with(name, ".idx"))
+       if (ends_with(name, ".rev"))
                return 3;
-       return 4;
+       if (ends_with(name, ".idx"))
+               return 4;
+       return 5;
 }
 
 static int pack_copy_cmp(const char *a, const char *b)
index 5f6e0b3bd874dba714c107dffebd423ef25d10b4..49b7fb4dcb9a3012a9bdc60bc6fa581a47875e07 100644 (file)
@@ -1162,13 +1162,14 @@ static int has_attribute(const char *attrs, const char *attr)
 }
 
 static struct ref *get_refs_list(struct transport *transport, int for_push,
-                                const struct strvec *ref_prefixes)
+                                struct transport_ls_refs_options *transport_options)
 {
        get_helper(transport);
 
        if (process_connect(transport, for_push)) {
                do_take_over(transport);
-               return transport->vtable->get_refs_list(transport, for_push, ref_prefixes);
+               return transport->vtable->get_refs_list(transport, for_push,
+                                                       transport_options);
        }
 
        return get_refs_list_using_list(transport, for_push);
index 27c9daffc47ac95b2070006f570373c14cd4aa22..b60f1ba9077d09c5f0c3259fe5bc86c5facdbc78 100644 (file)
@@ -4,6 +4,7 @@
 struct ref;
 struct transport;
 struct strvec;
+struct transport_ls_refs_options;
 
 struct transport_vtable {
        /**
@@ -18,19 +19,12 @@ struct transport_vtable {
         * the transport to try to share connections, for_push is a
         * hint as to whether the ultimate operation is a push or a fetch.
         *
-        * If communicating using protocol v2 a list of prefixes can be
-        * provided to be sent to the server to enable it to limit the ref
-        * advertisement.  Since ref filtering is done on the server's end, and
-        * only when using protocol v2, this list will be ignored when not
-        * using protocol v2 meaning this function can return refs which don't
-        * match the provided ref_prefixes.
-        *
         * If the transport is able to determine the remote hash for
         * the ref without a huge amount of effort, it should store it
         * in the ref's old_sha1 field; otherwise it should be all 0.
         **/
        struct ref *(*get_refs_list)(struct transport *transport, int for_push,
-                                    const struct strvec *ref_prefixes);
+                                    struct transport_ls_refs_options *transport_options);
 
        /**
         * Fetch the objects for the given refs. Note that this gets
index 679a35e7c1b6168d3b8fc93f72ec9a50178d894c..b13fab5dc3b1b90933b77f15fe87ca422cc70784 100644 (file)
@@ -127,7 +127,7 @@ struct bundle_transport_data {
 
 static struct ref *get_refs_from_bundle(struct transport *transport,
                                        int for_push,
-                                       const struct strvec *ref_prefixes)
+                                       struct transport_ls_refs_options *transport_options)
 {
        struct bundle_transport_data *data = transport->data;
        struct ref *result = NULL;
@@ -280,7 +280,7 @@ static void die_if_server_options(struct transport *transport)
  * remote refs.
  */
 static struct ref *handshake(struct transport *transport, int for_push,
-                            const struct strvec *ref_prefixes,
+                            struct transport_ls_refs_options *options,
                             int must_list_refs)
 {
        struct git_transport_data *data = transport->data;
@@ -303,7 +303,7 @@ static struct ref *handshake(struct transport *transport, int for_push,
                        trace2_data_string("transfer", NULL, "server-sid", server_sid);
                if (must_list_refs)
                        get_remote_refs(data->fd[1], &reader, &refs, for_push,
-                                       ref_prefixes,
+                                       options,
                                        transport->server_options,
                                        transport->stateless_rpc);
                break;
@@ -334,9 +334,9 @@ static struct ref *handshake(struct transport *transport, int for_push,
 }
 
 static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
-                                       const struct strvec *ref_prefixes)
+                                       struct transport_ls_refs_options *options)
 {
-       return handshake(transport, for_push, ref_prefixes, 1);
+       return handshake(transport, for_push, options, 1);
 }
 
 static int fetch_refs_via_pack(struct transport *transport,
@@ -1252,19 +1252,20 @@ int transport_push(struct repository *r,
                int porcelain = flags & TRANSPORT_PUSH_PORCELAIN;
                int pretend = flags & TRANSPORT_PUSH_DRY_RUN;
                int push_ret, ret, err;
-               struct strvec ref_prefixes = STRVEC_INIT;
+               struct transport_ls_refs_options transport_options =
+                       TRANSPORT_LS_REFS_OPTIONS_INIT;
 
                if (check_push_refs(local_refs, rs) < 0)
                        return -1;
 
-               refspec_ref_prefixes(rs, &ref_prefixes);
+               refspec_ref_prefixes(rs, &transport_options.ref_prefixes);
 
                trace2_region_enter("transport_push", "get_refs_list", r);
                remote_refs = transport->vtable->get_refs_list(transport, 1,
-                                                              &ref_prefixes);
+                                                              &transport_options);
                trace2_region_leave("transport_push", "get_refs_list", r);
 
-               strvec_clear(&ref_prefixes);
+               strvec_clear(&transport_options.ref_prefixes);
 
                if (flags & TRANSPORT_PUSH_ALL)
                        match_flags |= MATCH_REFS_ALL;
@@ -1380,12 +1381,12 @@ int transport_push(struct repository *r,
 }
 
 const struct ref *transport_get_remote_refs(struct transport *transport,
-                                           const struct strvec *ref_prefixes)
+                                           struct transport_ls_refs_options *transport_options)
 {
        if (!transport->got_remote_refs) {
                transport->remote_refs =
                        transport->vtable->get_refs_list(transport, 0,
-                                                        ref_prefixes);
+                                                        transport_options);
                transport->got_remote_refs = 1;
        }
 
index 24558c027d61bba1dd99b17a1c9d1cd484b1cb8a..24e15799e714aeb53f725b161a14172202cbae7e 100644 (file)
@@ -233,17 +233,32 @@ int transport_push(struct repository *repo,
                   struct refspec *rs, int flags,
                   unsigned int * reject_reasons);
 
+struct transport_ls_refs_options {
+       /*
+        * Optionally, a list of ref prefixes can be provided which can be sent
+        * to the server (when communicating using protocol v2) to enable it to
+        * limit the ref advertisement.  Since ref filtering is done on the
+        * server's end (and only when using protocol v2),
+        * transport_get_remote_refs() could return refs which don't match the
+        * provided ref_prefixes.
+        */
+       struct strvec ref_prefixes;
+
+       /*
+        * If unborn_head_target is not NULL, and the remote reports HEAD as
+        * pointing to an unborn branch, transport_get_remote_refs() stores the
+        * unborn branch in unborn_head_target. It should be freed by the
+        * caller.
+        */
+       char *unborn_head_target;
+};
+#define TRANSPORT_LS_REFS_OPTIONS_INIT { STRVEC_INIT }
+
 /*
  * Retrieve refs from a remote.
- *
- * Optionally a list of ref prefixes can be provided which can be sent to the
- * server (when communicating using protocol v2) to enable it to limit the ref
- * advertisement.  Since ref filtering is done on the server's end (and only
- * when using protocol v2), this can return refs which don't match the provided
- * ref_prefixes.
  */
 const struct ref *transport_get_remote_refs(struct transport *transport,
-                                           const struct strvec *ref_prefixes);
+                                           struct transport_ls_refs_options *transport_options);
 
 /*
  * Fetch the hash algorithm used by a remote.
index 4ab55ce2b594ded6f46a90efc31f87aa6f435b2d..e19583ae0fbebbdf6f9d6f4c89580332a8a16307 100644 (file)
@@ -305,14 +305,7 @@ static void create_pack_file(struct upload_pack_data *pack_data,
        if (pack_data->filter_options.choice) {
                const char *spec =
                        expand_list_objects_filter_spec(&pack_data->filter_options);
-               if (pack_objects.use_shell) {
-                       struct strbuf buf = STRBUF_INIT;
-                       sq_quote_buf(&buf, spec);
-                       strvec_pushf(&pack_objects.args, "--filter=%s", buf.buf);
-                       strbuf_release(&buf);
-               } else {
-                       strvec_pushf(&pack_objects.args, "--filter=%s", spec);
-               }
+               strvec_pushf(&pack_objects.args, "--filter=%s", spec);
        }
        if (uri_protocols) {
                for (i = 0; i < uri_protocols->nr; i++)
@@ -500,7 +493,7 @@ static int got_oid(struct upload_pack_data *data,
 
 static int ok_to_give_up(struct upload_pack_data *data)
 {
-       uint32_t min_generation = GENERATION_NUMBER_ZERO;
+       timestamp_t min_generation = GENERATION_NUMBER_ZERO;
 
        if (!data->have_obj.nr)
                return 0;
diff --git a/usage.c b/usage.c
index 1868a24f7a5148b1714dc4a9730f343339e61dce..1b206de36d6e1cb7799fce728c2a37e310479223 100644 (file)
--- a/usage.c
+++ b/usage.c
@@ -266,6 +266,10 @@ int BUG_exit_code;
 static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
 {
        char prefix[256];
+       va_list params_copy;
+       static int in_bug;
+
+       va_copy(params_copy, params);
 
        /* truncation via snprintf is OK here */
        if (file)
@@ -274,6 +278,13 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis
                snprintf(prefix, sizeof(prefix), "BUG: ");
 
        vreportf(prefix, fmt, params);
+
+       if (in_bug)
+               abort();
+       in_bug = 1;
+
+       trace2_cmd_error_va(fmt, params_copy);
+
        if (BUG_exit_code)
                exit(BUG_exit_code);
        abort();