Merge branch 'rs/strbuf-detach'
authorJunio C Hamano <gitster@pobox.com>
Mon, 30 Sep 2019 04:19:24 +0000 (13:19 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 30 Sep 2019 04:19:24 +0000 (13:19 +0900)
Straighten out the use of strbuf_detach() API function.

* rs/strbuf-detach:
grep: use return value of strbuf_detach()
log-tree: always use return value of strbuf_detach()

170 files changed:
.gitignore
Documentation/RelNotes/2.24.0.txt
Documentation/config.txt
Documentation/config/core.txt
Documentation/config/diff.txt
Documentation/config/feature.txt [new file with mode: 0644]
Documentation/config/fetch.txt
Documentation/config/format.txt
Documentation/config/gc.txt
Documentation/config/index.txt
Documentation/config/pack.txt
Documentation/config/remote.txt
Documentation/fetch-options.txt
Documentation/git-format-patch.txt
Documentation/git-merge.txt
Documentation/git-rebase.txt
Documentation/gitattributes.txt
Documentation/gitcli.txt
Documentation/githooks.txt
Documentation/gitrepository-layout.txt
Documentation/merge-options.txt
Documentation/rev-list-options.txt
Documentation/technical/partial-clone.txt
Makefile
apply.c
archive-tar.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/fetch.c
builtin/gc.c
builtin/index-pack.c
builtin/merge.c
builtin/pack-objects.c
builtin/pull.c
builtin/rebase.c
builtin/repack.c
builtin/rev-list.c
builtin/update-index.c
cache-tree.c
cache.h
commit-graph.c
common-main.c
compat/mingw.c
compat/mingw.h
compat/win32/pthread.h
config.c
connected.c
contrib/completion/git-completion.bash
convert.c
convert.h
credential-store.c
diff.c
environment.c
fetch-negotiator.c
fetch-negotiator.h
fetch-object.c [deleted file]
fetch-object.h [deleted file]
fetch-pack.c
git-compat-util.h
git-gui/git-gui.sh
git-gui/lib/checkout_op.tcl
git-gui/lib/commit.tcl
git-gui/lib/diff.tcl
git-gui/lib/index.tcl
gitk-git/gitk
gitk-git/po/zh_cn.po [new file with mode: 0644]
http.c
http.h
line-log.c
list-objects-filter-options.c
list-objects-filter-options.h
list-objects-filter.c
list-objects-filter.h
list-objects.c
ll-merge.c
ll-merge.h
packfile.c
packfile.h
parse-options.c
parse-options.h
path.c
path.h
promisor-remote.c [new file with mode: 0644]
promisor-remote.h [new file with mode: 0644]
read-cache.c
ref-filter.c
repo-settings.c [new file with mode: 0644]
repository.h
revision.c
sequencer.c
sequencer.h
setup.c
sha1-file.c
sha1-name.c
strbuf.c
strbuf.h
t/lib-rebase.sh
t/t0021-conversion.sh
t/t0040-parse-options.sh
t/t0410-partial-clone.sh
t/t1309-early-config.sh
t/t1600-index.sh
t/t3005-ls-files-relative.sh
t/t3201-branch-contains.sh
t/t3206-range-diff.sh
t/t3301-notes.sh
t/t3305-notes-fanout.sh
t/t3306-notes-prune.sh
t/t3400-rebase.sh
t/t3404-rebase-interactive.sh
t/t3418-rebase-continue.sh
t/t3422-rebase-incompatible-options.sh
t/t3427-rebase-subtree.sh
t/t3430-rebase-merges.sh
t/t3506-cherry-pick-ff.sh
t/t3600-rm.sh
t/t3800-mktag.sh
t/t3903-stash.sh
t/t4000-diff-format.sh
t/t4002-diff-basic.sh
t/t4009-diff-rename-4.sh
t/t4013-diff-various.sh
t/t4014-format-patch.sh
t/t4018-diff-funcname.sh
t/t4018/dts-labels [new file with mode: 0644]
t/t4018/dts-node-unitless [new file with mode: 0644]
t/t4018/dts-nodes [new file with mode: 0644]
t/t4018/dts-nodes-comment1 [new file with mode: 0644]
t/t4018/dts-nodes-comment2 [new file with mode: 0644]
t/t4018/dts-reference [new file with mode: 0644]
t/t4018/dts-root [new file with mode: 0644]
t/t4034-diff-words.sh
t/t4034/dts/expect [new file with mode: 0644]
t/t4034/dts/post [new file with mode: 0644]
t/t4034/dts/pre [new file with mode: 0644]
t/t4067-diff-partial-clone.sh
t/t4150-am.sh
t/t4202-log.sh
t/t4211-line-log.sh
t/t5004-archive-corner-cases.sh
t/t5307-pack-missing-commit.sh
t/t5324-split-commit-graph.sh
t/t5515-fetch-merge-logic.sh
t/t5552-skipping-fetch-negotiator.sh
t/t5553-set-upstream.sh [new file with mode: 0755]
t/t5601-clone.sh
t/t5607-clone-bundle.sh
t/t5616-partial-clone.sh
t/t5702-protocol-v2.sh
t/t6000-rev-list-misc.sh
t/t6011-rev-list-with-bad-commit.sh
t/t6112-rev-list-filters-objects.sh
t/t6300-for-each-ref.sh
t/t6501-freshen-objects.sh
t/t7503-pre-commit-and-pre-merge-commit-hooks.sh [new file with mode: 0755]
t/t7503-pre-commit-hook.sh [deleted file]
t/t9902-completion.sh
templates/hooks--pre-merge-commit.sample [new file with mode: 0755]
trace.c
trace2/tr2_dst.c
transport-helper.c
transport-internal.h
transport.c
unpack-trees.c
upload-pack.c
url.c
url.h
userdiff.c
wrapper.c
index 521d8f4fb421c25e5f733242796fddfa47f39f93..fc445edea9d3497b72c16629b0729c75d1ebae28 100644 (file)
 *.ipdb
 *.dll
 .vs/
-*.manifest
 Debug/
 Release/
 /UpgradeLog*.htm
index a95a8b0084f8d3ec04e168b9e994216da494b5b3..ff48d8582c6718d4c8c82cb9ec5137c67fd8674f 100644 (file)
@@ -11,7 +11,40 @@ Backward compatibility note
 
 UI, Workflows & Features
 
- * (no entry yet so far)
+ * We now have an active interim maintainer for the Git-Gui part of
+   the system.  Praise and thank Pratyush Yadav for volunteering.
+
+ * The command line parser learned "--end-of-options" notation; the
+   standard convention for scripters to have hardcoded set of options
+   first on the command line, and force the command to treat end-user
+   input as non-options, has been to use "--" as the delimiter, but
+   that would not work for commands that use "--" as a delimiter
+   between revs and pathspec.
+
+ * A mechanism to affect the default setting for a (related) group of
+   configuration variables is introduced.
+
+ * "git fetch" learned "--set-upstream" option to help those who first
+   clone from their private fork they intend to push to, add the true
+   upstream via "git remote add" and then "git fetch" from it.
+
+ * Device-tree files learned their own userdiff patterns.
+   (merge 3c81760bc6 sb/userdiff-dts later to maint).
+
+ * "git rebase --rebase-merges" learned to drive different merge
+   strategies and pass strategy specific options to them.
+
+ * A new "pre-merge-commit" hook has been introduced.
+
+ * Command line completion updates for "git -c var.name=val" have been
+   added.
+
+ * The lazy clone machinery has been taught that there can be more
+   than one promisor remote and consult them in order when downloading
+   missing objects on demand.
+
+ * The list-objects-filter API (used to create a sparse/lazy clone)
+   learned to take a combined filter specification.
 
 
 Performance, Internal Implementation, Development Support etc.
@@ -22,6 +55,22 @@ Performance, Internal Implementation, Development Support etc.
  * The first line of verbose output from each test piece now carries
    the test name and number to help scanning with eyeballs.
 
+ * Further clean-up of the initialization code.
+
+ * xmalloc() used to have a mechanism to ditch memory and address
+   space resources as the last resort upon seeing an allocation
+   failure from the underlying malloc(), which made the code complex
+   and thread-unsafe with dubious benefit, as major memory resource
+   users already do limit their uses with various other mechanisms.
+   It has been simplified away.
+
+ * Unnecessary full-tree diff in "git log -L" machinery has been
+   optimized away.
+
+ * The http transport lacked some optimization the native transports
+   learned to avoid unnecessary ref advertisement, which has been
+   corrected.
+
 
 Fixes since v2.23
 -----------------
@@ -48,5 +97,47 @@ Fixes since v2.23
  * Compilation fix.
    (merge 70597e8386 rs/nedalloc-fixlets later to maint).
 
+ * "git gui" learned to call the clean-up procedure before exiting.
+   (merge 0d88f3d2c5 py/git-gui-do-quit later to maint).
+
+ * We promoted the "indent heuristics" that decides where to split
+   diff hunks from experimental to the default a few years ago, but
+   some stale documentation still marked it as experimental, which has
+   been corrected.
+   (merge 64e5e1fba1 sg/diff-indent-heuristic-non-experimental later to maint).
+
+ * Fix a mismerge that happened in 2.22 timeframe.
+   (merge acb7da05ac en/checkout-mismerge-fix later to maint).
+
+ * "git archive" recorded incorrect length in extended pax header in
+   some corner cases, which has been corrected.
+   (merge 71d41ff651 rs/pax-extended-header-length-fix later to maint).
+
+ * On-demand object fetching in lazy clone incorrectly tried to fetch
+   commits from submodule projects, while still working in the
+   superproject, which has been corrected.
+   (merge a63694f523 jt/diff-lazy-fetch-submodule-fix later to maint).
+
+ * Prepare get_short_oid() codepath to be thread-safe.
+   (merge 7cfcb16b0e rs/sort-oid-array-thread-safe later to maint).
+
+ * "for-each-ref" and friends that show refs did not protect themselves
+   against ancient tags that did not record tagger names when asked to
+   show "%(taggername)", which have been corrected.
+   (merge 8b3f33ef11 mp/for-each-ref-missing-name-or-email later to maint).
+
+ * The "git am" based backend of "git rebase" ignored the result of
+   updating ".gitattributes" done in one step when replaying
+   subsequent steps.
+   (merge 2c65d90f75 bc/reread-attributes-during-rebase later to maint).
+
+ * Tell cURL library to use the same malloc() implementation, with the
+   xmalloc() wrapper, as the rest of the system, for consistency.
+   (merge 93b980e58f cb/curl-use-xmalloc later to maint).
+
  * Other code cleanup, docfix, build fix, etc.
    (merge d1387d3895 en/fast-import-merge-doc later to maint).
+   (merge 1c24a54ea4 bm/repository-layout-typofix later to maint).
+   (merge 415b770b88 ds/midx-expire-repack later to maint).
+   (merge 19800bdc3f nd/diff-parseopt later to maint).
+   (merge 58166c2e9d tg/t0021-racefix later to maint).
index e3f5bc3396d0c7502f16eed989220c8e2010bcc1..77f3b1486b78ab81e1248e1a6cfb651c9e17fd85 100644 (file)
@@ -345,6 +345,8 @@ include::config/difftool.txt[]
 
 include::config/fastimport.txt[]
 
+include::config/feature.txt[]
+
 include::config/fetch.txt[]
 
 include::config/format.txt[]
index 75538d27e7e06b2041f556dc5e3fcd4aef1abf20..852d2ba37a1204e0e210e9abf671d7f0c9c850d8 100644 (file)
@@ -86,7 +86,9 @@ core.untrackedCache::
        it will automatically be removed, if set to `false`. Before
        setting it to `true`, you should check that mtime is working
        properly on your system.
-       See linkgit:git-update-index[1]. `keep` by default.
+       See linkgit:git-update-index[1]. `keep` by default, unless
+       `feature.manyFiles` is enabled which sets this setting to
+       `true` by default.
 
 core.checkStat::
        When missing or is set to `default`, many fields in the stat
@@ -577,7 +579,7 @@ the `GIT_NOTES_REF` environment variable.  See linkgit:git-notes[1].
 
 core.commitGraph::
        If true, then git will read the commit-graph file (if it exists)
-       to parse the graph structure of commits. Defaults to false. See
+       to parse the graph structure of commits. Defaults to true. See
        linkgit:git-commit-graph[1] for more information.
 
 core.useReplaceRefs::
index 5afb5a2cbc69b763263e3e265343884c0ff64dda..ff09f1cf737c062898ac40555402de097d360eb9 100644 (file)
@@ -189,7 +189,7 @@ diff.guitool::
 include::../mergetools-diff.txt[]
 
 diff.indentHeuristic::
-       Set this option to `true` to enable experimental heuristics
+       Set this option to `false` to disable the default heuristics
        that shift diff hunk boundaries to make patches easier to read.
 
 diff.algorithm::
diff --git a/Documentation/config/feature.txt b/Documentation/config/feature.txt
new file mode 100644 (file)
index 0000000..545522f
--- /dev/null
@@ -0,0 +1,29 @@
+feature.*::
+       The config settings that start with `feature.` modify the defaults of
+       a group of other config settings. These groups are created by the Git
+       developer community as recommended defaults and are subject to change.
+       In particular, new config options may be added with different defaults.
+
+feature.experimental::
+       Enable config options that are new to Git, and are being considered for
+       future defaults. Config settings included here may be added or removed
+       with each release, including minor version updates. These settings may
+       have unintended interactions since they are so new. Please enable this
+       setting if you are interested in providing feedback on experimental
+       features. The new default values are:
++
+* `pack.useSparse=true` uses a new algorithm when constructing a pack-file
+which can improve `git push` performance in repos with many files.
++
+* `fetch.negotiationAlgorithm=skipping` may improve fetch negotiation times by
+skipping more commits at a time, reducing the number of round trips.
+
+feature.manyFiles::
+       Enable config options that optimize for repos with many files in the
+       working directory. With many files, commands such as `git status` and
+       `git checkout` may be slow and these new defaults improve performance:
++
+* `index.version=4` enables path-prefix compression in the index.
++
+* `core.untrackedCache=true` enables the untracked cache. This setting assumes
+that mtime is working on your machine.
index ba890b5884fc3496e6529cb8e1f41ad7dd9748f4..d402110638b4e465075bf1a59885563d15f190e1 100644 (file)
@@ -59,7 +59,8 @@ fetch.negotiationAlgorithm::
        effort to converge faster, but may result in a larger-than-necessary
        packfile; The default is "default" which instructs Git to use the default algorithm
        that never skips commits (unless the server has acknowledged it or one
-       of its descendants).
+       of its descendants). If `feature.experimental` is enabled, then this
+       setting defaults to "skipping".
        Unknown values will cause 'git fetch' to error out.
 +
 See also the `--negotiation-tip` option for linkgit:git-fetch[1].
index 414a5a8a9d7de5bbcefd928ada93aed725427b33..cb629fa769b7ffb61f4c825894a450aea2e8c1e2 100644 (file)
@@ -77,6 +77,7 @@ format.coverLetter::
        A boolean that controls whether to generate a cover-letter when
        format-patch is invoked, but in addition can be set to "auto", to
        generate a cover-letter only when there's more than one patch.
+       Default is false.
 
 format.outputDirectory::
        Set a custom directory to store the resulting files instead of the
index 02b92b18b5c2cf6f9d509483a4bb995f3677d519..00ea0a678ee214f86cc91b27389572698d35bece 100644 (file)
@@ -63,7 +63,7 @@ gc.writeCommitGraph::
        If true, then gc will rewrite the commit-graph file when
        linkgit:git-gc[1] is run. When using `git gc --auto`
        the commit-graph will be updated if housekeeping is
-       required. Default is false. See linkgit:git-commit-graph[1]
+       required. Default is true. See linkgit:git-commit-graph[1]
        for details.
 
 gc.logExpiry::
index f18150304106891ad388a7620594937b43e7615e..7cb50b37e98dba813886adb4578435495be8e78c 100644 (file)
@@ -24,3 +24,4 @@ index.threads::
 index.version::
        Specify the version with which new index files should be
        initialized.  This does not affect existing repositories.
+       If `feature.manyFiles` is enabled, then the default is 4.
index 9cdcfa7324784299f431d94b5237cc136aa585d1..1d66f0c992c38237dfa7def97d9b62c9803c7fc8 100644 (file)
@@ -112,7 +112,8 @@ pack.useSparse::
        objects. This can have significant performance benefits when
        computing a pack to send a small change. However, it is possible
        that extra objects are added to the pack-file if the included
-       commits contain certain types of direct renames.
+       commits contain certain types of direct renames. Default is `false`
+       unless `feature.experimental` is enabled.
 
 pack.writeBitmaps (deprecated)::
        This is a deprecated synonym for `repack.writeBitmaps`.
index 6c4cad83a2c9f41206e6c1032573d82fa7f3090c..a8e6437a903592934198fb212ede8241d6acebca 100644 (file)
@@ -76,3 +76,11 @@ remote.<name>.pruneTags::
 +
 See also `remote.<name>.prune` and the PRUNING section of
 linkgit:git-fetch[1].
+
+remote.<name>.promisor::
+       When set to true, this remote will be used to fetch promisor
+       objects.
+
+remote.<name>.partialclonefilter::
+       The filter that will be applied when fetching from this
+       promisor remote.
index 3c9b4f9e09515d99d32a3d6dfa6603ef3a6f23b6..99df1f3d4e3773bf78c4c3c128f91b33fc084d2f 100644 (file)
@@ -169,6 +169,13 @@ ifndef::git-pull[]
        Disable recursive fetching of submodules (this has the same effect as
        using the `--recurse-submodules=no` option).
 
+--set-upstream::
+       If the remote is fetched successfully, pull and add upstream
+       (tracking) reference, used by argument-less
+       linkgit:git-pull[1] and other commands. For more information,
+       see `branch.<name>.merge` and `branch.<name>.remote` in
+       linkgit:git-config[1].
+
 --submodule-prefix=<path>::
        Prepend <path> to paths printed in informative messages
        such as "Fetching submodule foo".  This option is used
index b9b97e63aec5e73e7c5607c19661639a256dc1ed..0ac56f4b7080bc3d8718e7cad7dab4fedca0f670 100644 (file)
@@ -17,9 +17,9 @@ SYNOPSIS
                   [--signature-file=<file>]
                   [-n | --numbered | -N | --no-numbered]
                   [--start-number <n>] [--numbered-files]
-                  [--in-reply-to=Message-Id] [--suffix=.<sfx>]
+                  [--in-reply-to=<message id>] [--suffix=.<sfx>]
                   [--ignore-if-in-upstream]
-                  [--rfc] [--subject-prefix=Subject-Prefix]
+                  [--rfc] [--subject-prefix=<subject prefix>]
                   [(--reroll-count|-v) <n>]
                   [--to=<email>] [--cc=<email>]
                   [--[no-]cover-letter] [--quiet]
@@ -159,9 +159,9 @@ Beware that the default for 'git send-email' is to thread emails
 itself.  If you want `git format-patch` to take care of threading, you
 will want to ensure that threading is disabled for `git send-email`.
 
---in-reply-to=Message-Id::
+--in-reply-to=<message id>::
        Make the first mail (or all the mails with `--no-thread`) appear as a
-       reply to the given Message-Id, which avoids breaking threads to
+       reply to the given <message id>, which avoids breaking threads to
        provide a new patch series.
 
 --ignore-if-in-upstream::
@@ -171,9 +171,9 @@ will want to ensure that threading is disabled for `git send-email`.
        patches being generated, and any patch that matches is
        ignored.
 
---subject-prefix=<Subject-Prefix>::
+--subject-prefix=<subject prefix>::
        Instead of the standard '[PATCH]' prefix in the subject
-       line, instead use '[<Subject-Prefix>]'. This
+       line, instead use '[<subject prefix>]'. This
        allows for useful naming of a patch series, and can be
        combined with the `--numbered` option.
 
@@ -314,7 +314,8 @@ you can use `--suffix=-patch` to get `0001-description-of-my-change-patch`.
 --base=<commit>::
        Record the base tree information to identify the state the
        patch series applies to.  See the BASE TREE INFORMATION section
-       below for details.
+       below for details. If <commit> is "auto", a base commit is
+       automatically chosen.
 
 --root::
        Treat the revision argument as a <revision range>, even if it
@@ -330,8 +331,9 @@ CONFIGURATION
 -------------
 You can specify extra mail header lines to be added to each message,
 defaults for the subject prefix and file suffix, number patches when
-outputting more than one patch, add "To" or "Cc:" headers, configure
-attachments, and sign off patches with configuration variables.
+outputting more than one patch, add "To:" or "Cc:" headers, configure
+attachments, change the patch output directory, and sign off patches
+with configuration variables.
 
 ------------
 [format]
@@ -343,7 +345,8 @@ attachments, and sign off patches with configuration variables.
        cc = <email>
        attach [ = mime-boundary-string ]
        signOff = true
-       coverletter = auto
+       outputDirectory = <directory>
+       coverLetter = auto
 ------------
 
 
index 01fd52dc7063802226bf4f7205a2a1aab697bc67..092529c619e29cfa1c0959358d4671ff1cba1144 100644 (file)
@@ -10,7 +10,7 @@ SYNOPSIS
 --------
 [verse]
 'git merge' [-n] [--stat] [--no-commit] [--squash] [--[no-]edit]
-       [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
+       [--no-verify] [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
        [--[no-]allow-unrelated-histories]
        [--[no-]rerere-autoupdate] [-m <msg>] [-F <file>] [<commit>...]
 'git merge' (--continue | --abort | --quit)
index 6156609cf7149ccf5c1f79df2d9807cdbcc609ba..3136c19fb4ce578ee37c4e3b9b86ab2e47294dc8 100644 (file)
@@ -543,8 +543,6 @@ In addition, the following pairs of options are incompatible:
  * --preserve-merges and --interactive
  * --preserve-merges and --signoff
  * --preserve-merges and --rebase-merges
- * --rebase-merges and --strategy
- * --rebase-merges and --strategy-option
 
 BEHAVIORAL DIFFERENCES
 -----------------------
index fb1d188d440cc2fd3579844045d01a77423a58c7..c5a528c667b62abe81e6454b061834200011657e 100644 (file)
@@ -810,6 +810,8 @@ patterns are available:
 
 - `css` suitable for cascading style sheets.
 
+- `dts` suitable for devicetree (DTS) files.
+
 - `fortran` suitable for source code in the Fortran language.
 
 - `fountain` suitable for Fountain documents.
index 1ed3ca33b7a94ad8187d7fa490cc212711fd578e..4b32876b6e912b528ee0e00399719576a12b1990 100644 (file)
@@ -37,6 +37,12 @@ arguments.  Here are the rules:
    file called HEAD in your work tree, `git diff HEAD` is ambiguous, and
    you have to say either `git diff HEAD --` or `git diff -- HEAD` to
    disambiguate.
+
+ * Because `--` disambiguates revisions and paths in some commands, it
+   cannot be used for those commands to separate options and revisions.
+   You can use `--end-of-options` for this (it also works for commands
+   that do not distinguish between revisions in paths, in which case it
+   is simply an alias for `--`).
 +
 When writing a script that is expected to handle random user-input, it is
 a good practice to make it explicit which arguments are which by placing
index 82cd573776cec696774c467979c6da10f23554c3..57d6e2b98da7a093eff4ac0987824e34f6858677 100644 (file)
@@ -103,6 +103,28 @@ The default 'pre-commit' hook, when enabled--and with the
 `hooks.allownonascii` config option unset or set to false--prevents
 the use of non-ASCII filenames.
 
+pre-merge-commit
+~~~~~~~~~~~~~~~~
+
+This hook is invoked by linkgit:git-merge[1], and can be bypassed
+with the `--no-verify` option.  It takes no parameters, and is
+invoked after the merge has been carried out successfully and before
+obtaining the proposed commit log message to
+make a commit.  Exiting with a non-zero status from this script
+causes the `git merge` command to abort before creating a commit.
+
+The default 'pre-merge-commit' hook, when enabled, runs the
+'pre-commit' hook, if the latter is enabled.
+
+This hook is invoked with the environment variable
+`GIT_EDITOR=:` if the command will not bring up an editor
+to modify the commit message.
+
+If the merge cannot be carried out automatically, the conflicts
+need to be resolved and the result committed separately (see
+linkgit:git-merge[1]). At that point, this hook will not be executed,
+but the 'pre-commit' hook will, if it is enabled.
+
 prepare-commit-msg
 ~~~~~~~~~~~~~~~~~~
 
index 216b11ee88f4e9c5b10278e037f3b1c51173bc6d..d6388f10bbeb45caf9ccb1cf945161b70be625dc 100644 (file)
@@ -59,7 +59,7 @@ objects/[0-9a-f][0-9a-f]::
        here are often called 'unpacked' (or 'loose') objects.
 
 objects/pack::
-       Packs (files that store many object in compressed form,
+       Packs (files that store many objects in compressed form,
        along with index files to allow them to be randomly
        accessed) are found in this directory.
 
index 79a00d2a4abd6f7191f2639185e199fa85f6289b..d6a9f4b96faaf2814b41a2ff79a49c9d4d899795 100644 (file)
@@ -105,6 +105,10 @@ option can be used to override --squash.
 +
 With --squash, --commit is not allowed, and will fail.
 
+--no-verify::
+       This option bypasses the pre-merge and commit-msg hooks.
+       See also linkgit:githooks[5].
+
 -s <strategy>::
 --strategy=<strategy>::
        Use the given merge strategy; can be supplied more than
index bb1251c0364dc71880f6e63db7c6116ed859b90f..90ff9e2bea2e2fd0caf4744ed4e5c5e59f4c1243 100644 (file)
@@ -756,6 +756,22 @@ explicitly-given commit or tree.
 Note that the form '--filter=sparse:path=<path>' that wants to read
 from an arbitrary path on the filesystem has been dropped for security
 reasons.
++
+Multiple '--filter=' flags can be specified to combine filters. Only
+objects which are accepted by every filter are included.
++
+The form '--filter=combine:<filter1>+<filter2>+...<filterN>' can also be
+used to combined several filters, but this is harder than just repeating
+the '--filter' flag and is usually not necessary. Filters are joined by
+'{plus}' and individual filters are %-encoded (i.e. URL-encoded).
+Besides the '{plus}' and '%' characters, the following characters are
+reserved and also must be encoded: `~!@#$^&*()[]{}\;",<>?`+&#39;&#96;+
+as well as all characters with ASCII code &lt;= `0x20`, which includes
+space and newline.
++
+Other arbitrary characters can also be encoded. For instance,
+'combine:tree:3+blob:none' and 'combine:tree%3A3+blob%3Anone' are
+equivalent.
 
 --no-filter::
        Turn off any previous `--filter=` argument.
index 896c7b3878869de8d3564b42abcef9d4ea5dedec..210373e258890d7204c6dcf4a1befbd31e21139b 100644 (file)
@@ -30,12 +30,20 @@ advance* during clone and fetch operations and thereby reduce download
 times and disk usage.  Missing objects can later be "demand fetched"
 if/when needed.
 
+A remote that can later provide the missing objects is called a
+promisor remote, as it promises to send the objects when
+requested. Initialy Git supported only one promisor remote, the origin
+remote from which the user cloned and that was configured in the
+"extensions.partialClone" config option. Later support for more than
+one promisor remote has been implemented.
+
 Use of partial clone requires that the user be online and the origin
-remote be available for on-demand fetching of missing objects.  This may
-or may not be problematic for the user.  For example, if the user can
-stay within the pre-selected subset of the source tree, they may not
-encounter any missing objects.  Alternatively, the user could try to
-pre-fetch various objects if they know that they are going offline.
+remote or other promisor remotes be available for on-demand fetching
+of missing objects.  This may or may not be problematic for the user.
+For example, if the user can stay within the pre-selected subset of
+the source tree, they may not encounter any missing objects.
+Alternatively, the user could try to pre-fetch various objects if they
+know that they are going offline.
 
 
 Non-Goals
@@ -100,18 +108,18 @@ or commits that reference missing trees.
 Handling Missing Objects
 ------------------------
 
-- An object may be missing due to a partial clone or fetch, or missing due
-  to repository corruption.  To differentiate these cases, the local
-  repository specially indicates such filtered packfiles obtained from the
-  promisor remote as "promisor packfiles".
+- An object may be missing due to a partial clone or fetch, or missing
+  due to repository corruption.  To differentiate these cases, the
+  local repository specially indicates such filtered packfiles
+  obtained from promisor remotes as "promisor packfiles".
 +
 These promisor packfiles consist of a "<name>.promisor" file with
 arbitrary contents (like the "<name>.keep" files), in addition to
 their "<name>.pack" and "<name>.idx" files.
 
 - The local repository considers a "promisor object" to be an object that
-  it knows (to the best of its ability) that the promisor remote has promised
-  that it has, either because the local repository has that object in one of
+  it knows (to the best of its ability) that promisor remotes have promised
+  that they have, either because the local repository has that object in one of
   its promisor packfiles, or because another promisor object refers to it.
 +
 When Git encounters a missing object, Git can see if it is a promisor object
@@ -123,12 +131,12 @@ expensive-to-modify list of missing objects.[a]
 - Since almost all Git code currently expects any referenced object to be
   present locally and because we do not want to force every command to do
   a dry-run first, a fallback mechanism is added to allow Git to attempt
-  to dynamically fetch missing objects from the promisor remote.
+  to dynamically fetch missing objects from promisor remotes.
 +
 When the normal object lookup fails to find an object, Git invokes
-fetch-object to try to get the object from the server and then retry
-the object lookup.  This allows objects to be "faulted in" without
-complicated prediction algorithms.
+promisor_remote_get_direct() to try to get the object from a promisor
+remote and then retry the object lookup.  This allows objects to be
+"faulted in" without complicated prediction algorithms.
 +
 For efficiency reasons, no check as to whether the missing object is
 actually a promisor object is performed.
@@ -157,8 +165,7 @@ and prefetch those objects in bulk.
 +
 We are not happy with this global variable and would like to remove it,
 but that requires significant refactoring of the object code to pass an
-additional flag.  We hope that concurrent efforts to add an ODB API can
-encompass this.
+additional flag.
 
 
 Fetching Missing Objects
@@ -182,21 +189,63 @@ has been updated to not use any object flags when the corresponding argument
   though they are not necessary.
 
 
+Using many promisor remotes
+---------------------------
+
+Many promisor remotes can be configured and used.
+
+This allows for example a user to have multiple geographically-close
+cache servers for fetching missing blobs while continuing to do
+filtered `git-fetch` commands from the central server.
+
+When fetching objects, promisor remotes are tried one after the other
+until all the objects have been fetched.
+
+Remotes that are considered "promisor" remotes are those specified by
+the following configuration variables:
+
+- `extensions.partialClone = <name>`
+
+- `remote.<name>.promisor = true`
+
+- `remote.<name>.partialCloneFilter = ...`
+
+Only one promisor remote can be configured using the
+`extensions.partialClone` config variable. This promisor remote will
+be the last one tried when fetching objects.
+
+We decided to make it the last one we try, because it is likely that
+someone using many promisor remotes is doing so because the other
+promisor remotes are better for some reason (maybe they are closer or
+faster for some kind of objects) than the origin, and the origin is
+likely to be the remote specified by extensions.partialClone.
+
+This justification is not very strong, but one choice had to be made,
+and anyway the long term plan should be to make the order somehow
+fully configurable.
+
+For now though the other promisor remotes will be tried in the order
+they appear in the config file.
+
 Current Limitations
 -------------------
 
-- The remote used for a partial clone (or the first partial fetch
-  following a regular clone) is marked as the "promisor remote".
+- It is not possible to specify the order in which the promisor
+  remotes are tried in other ways than the order in which they appear
+  in the config file.
 +
-We are currently limited to a single promisor remote and only that
-remote may be used for subsequent partial fetches.
+It is also not possible to specify an order to be used when fetching
+from one remote and a different order when fetching from another
+remote.
+
+- It is not possible to push only specific objects to a promisor
+  remote.
 +
-We accept this limitation because we believe initial users of this
-feature will be using it on repositories with a strong single central
-server.
+It is not possible to push at the same time to multiple promisor
+remote in a specific order.
 
-- Dynamic object fetching will only ask the promisor remote for missing
-  objects.  We assume that the promisor remote has a complete view of the
+- Dynamic object fetching will only ask promisor remotes for missing
+  objects.  We assume that promisor remotes have a complete view of the
   repository and can satisfy all such requests.
 
 - Repack essentially treats promisor and non-promisor packfiles as 2
@@ -218,15 +267,17 @@ server.
 Future Work
 -----------
 
-- Allow more than one promisor remote and define a strategy for fetching
-  missing objects from specific promisor remotes or of iterating over the
-  set of promisor remotes until a missing object is found.
+- Improve the way to specify the order in which promisor remotes are
+  tried.
 +
-A user might want to have multiple geographically-close cache servers
-for fetching missing blobs while continuing to do filtered `git-fetch`
-commands from the central server, for example.
+For example this could allow to specify explicitly something like:
+"When fetching from this remote, I want to use these promisor remotes
+in this order, though, when pushing or fetching to that remote, I want
+to use those promisor remotes in that order."
+
+- Allow pushing to promisor remotes.
 +
-Or the user might want to work in a triangular work flow with multiple
+The user might want to work in a triangular work flow with multiple
 promisor remotes that each have an incomplete view of the repository.
 
 - Allow repack to work on promisor packfiles (while keeping them distinct
index f9255344ae5009a192ab83a332be749632d94531..f879697ea3b23f6ad6308afb33ae0f08ec262591 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -884,7 +884,6 @@ LIB_OBJS += ewah/ewah_io.o
 LIB_OBJS += ewah/ewah_rlw.o
 LIB_OBJS += exec-cmd.o
 LIB_OBJS += fetch-negotiator.o
-LIB_OBJS += fetch-object.o
 LIB_OBJS += fetch-pack.o
 LIB_OBJS += fsck.o
 LIB_OBJS += fsmonitor.o
@@ -948,6 +947,7 @@ LIB_OBJS += preload-index.o
 LIB_OBJS += pretty.o
 LIB_OBJS += prio-queue.o
 LIB_OBJS += progress.o
+LIB_OBJS += promisor-remote.o
 LIB_OBJS += prompt.o
 LIB_OBJS += protocol.o
 LIB_OBJS += quote.o
@@ -965,6 +965,7 @@ LIB_OBJS += refspec.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace-object.o
+LIB_OBJS += repo-settings.o
 LIB_OBJS += repository.o
 LIB_OBJS += rerere.o
 LIB_OBJS += resolve-undo.o
diff --git a/apply.c b/apply.c
index cde95369bb3f3a9c763108fd91e1e48e0e455e29..57a61f2881bed383862044413210b42abca18434 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -4643,6 +4643,7 @@ static int apply_patch(struct apply_state *state,
        struct patch *list = NULL, **listp = &list;
        int skipped_patch = 0;
        int res = 0;
+       int flush_attributes = 0;
 
        state->patch_input_file = filename;
        if (read_patch_file(&buf, fd) < 0)
@@ -4670,6 +4671,14 @@ static int apply_patch(struct apply_state *state,
                        patch_stats(state, patch);
                        *listp = patch;
                        listp = &patch->next;
+
+                       if ((patch->new_name &&
+                            ends_with_path_components(patch->new_name,
+                                                      GITATTRIBUTES_FILE)) ||
+                           (patch->old_name &&
+                            ends_with_path_components(patch->old_name,
+                                                      GITATTRIBUTES_FILE)))
+                               flush_attributes = 1;
                }
                else {
                        if (state->apply_verbosity > verbosity_normal)
@@ -4746,6 +4755,8 @@ static int apply_patch(struct apply_state *state,
        if (state->summary && state->apply_verbosity > verbosity_silent)
                summary_patch_list(list);
 
+       if (flush_attributes)
+               reset_parsed_attributes();
 end:
        free_patch_list(list);
        strbuf_release(&buf);
index 3e53aac1e6523571ce0b9cb02d151c1f00652603..e16d3f756ddd61d38477e73b71aa01e912ba2b13 100644 (file)
@@ -142,19 +142,25 @@ static int stream_blocked(const struct object_id *oid)
  * string and appends it to a struct strbuf.
  */
 static void strbuf_append_ext_header(struct strbuf *sb, const char *keyword,
-                                    const char *value, unsigned int valuelen)
+                                    const char *value, size_t valuelen)
 {
-       int len, tmp;
+       size_t orig_len = sb->len;
+       size_t len, tmp;
 
        /* "%u %s=%s\n" */
        len = 1 + 1 + strlen(keyword) + 1 + valuelen + 1;
-       for (tmp = len; tmp > 9; tmp /= 10)
+       for (tmp = 1; len / 10 >= tmp; tmp *= 10)
                len++;
 
        strbuf_grow(sb, len);
-       strbuf_addf(sb, "%u %s=", len, keyword);
+       strbuf_addf(sb, "%"PRIuMAX" %s=", (uintmax_t)len, keyword);
        strbuf_add(sb, value, valuelen);
        strbuf_addch(sb, '\n');
+
+       if (len != sb->len - orig_len)
+               BUG("pax extended header length miscalculated as %"PRIuMAX
+                   ", should be %"PRIuMAX,
+                   (uintmax_t)len, (uintmax_t)(sb->len - orig_len));
 }
 
 /*
index 995d47c85aad24a645786ed6480bd659c755997e..d6a1aa74cd41fe17fdb9d92f6b55fb7d67b31726 100644 (file)
@@ -15,6 +15,7 @@
 #include "sha1-array.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "promisor-remote.h"
 
 struct batch_options {
        int enabled;
@@ -524,8 +525,8 @@ static int batch_objects(struct batch_options *opt)
        if (opt->all_objects) {
                struct object_cb_data cb;
 
-               if (repository_format_partial_clone)
-                       warning("This repository has extensions.partialClone set. Some objects may not be loaded.");
+               if (has_promisor_remote())
+                       warning("This repository uses promisor remotes. Some objects may not be loaded.");
 
                cb.opt = opt;
                cb.expand = &data;
index 8d3ad7cd9e735fdece09793ec11995e7b91b5c6f..0a3679eb776ec962d943f3d1902c9c6e7f1feea7 100644 (file)
@@ -731,13 +731,6 @@ static int merge_working_tree(const struct checkout_opts *opts,
                                      "the following files:\n%s"), sb.buf);
                        strbuf_release(&sb);
 
-                       if (repo_index_has_changes(the_repository,
-                                                  get_commit_tree(old_branch_info->commit),
-                                                  &sb))
-                               warning(_("staged changes in the following files may be lost: %s"),
-                                       sb.buf);
-                       strbuf_release(&sb);
-
                        /* Do more real merge */
 
                        /*
index f665b28ccccfacaf5dfe84b7f94081e1afacdd49..2048b6760aa9f0a426524e5ea1bd7a3a4c4c5536 100644 (file)
@@ -1160,13 +1160,11 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                transport->server_options = &server_options;
 
        if (filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(&filter_options,
-                                               &expanded_filter_spec);
+               const char *spec =
+                       expand_list_objects_filter_spec(&filter_options);
                transport_set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                                    expanded_filter_spec.buf);
+                                    spec);
                transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
-               strbuf_release(&expanded_filter_spec);
        }
 
        if (transport->smart_options && !deepen && !filter_options.choice)
index 717dd14e896117c56101f7f56df0261576c323d0..9b27ae968199a75e62a617b649b56788e501e076 100644 (file)
@@ -23,6 +23,8 @@
 #include "packfile.h"
 #include "list-objects-filter-options.h"
 #include "commit-reach.h"
+#include "branch.h"
+#include "promisor-remote.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
 
@@ -50,7 +52,8 @@ static int fetch_prune_tags_config = -1; /* unspecified */
 static int prune_tags = -1; /* unspecified */
 #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
 
-static int all, append, dry_run, force, keep, multiple, update_head_ok, verbosity, deepen_relative;
+static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int verbosity, deepen_relative, set_upstream;
 static int progress = -1;
 static int enable_auto_gc = 1;
 static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
@@ -123,6 +126,8 @@ static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "all", &all,
                 N_("fetch from all remotes")),
+       OPT_BOOL(0, "set-upstream", &set_upstream,
+                N_("set upstream for git pull/fetch")),
        OPT_BOOL('a', "append", &append,
                 N_("append to .git/FETCH_HEAD instead of overwriting")),
        OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
@@ -1238,13 +1243,10 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        if (filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(&filter_options,
-                                               &expanded_filter_spec);
-               set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                          expanded_filter_spec.buf);
+               const char *spec =
+                       expand_list_objects_filter_spec(&filter_options);
+               set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec);
                set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
-               strbuf_release(&expanded_filter_spec);
        }
        if (negotiation_tip.nr) {
                if (transport->smart_options)
@@ -1367,6 +1369,51 @@ static int do_fetch(struct transport *transport,
                retcode = 1;
                goto cleanup;
        }
+
+       if (set_upstream) {
+               struct branch *branch = branch_get("HEAD");
+               struct ref *rm;
+               struct ref *source_ref = NULL;
+
+               /*
+                * We're setting the upstream configuration for the
+                * current branch. The relevent upstream is the
+                * fetched branch that is meant to be merged with the
+                * current one, i.e. the one fetched to FETCH_HEAD.
+                *
+                * When there are several such branches, consider the
+                * request ambiguous and err on the safe side by doing
+                * nothing and just emit a warning.
+                */
+               for (rm = ref_map; rm; rm = rm->next) {
+                       if (!rm->peer_ref) {
+                               if (source_ref) {
+                                       warning(_("multiple branch detected, incompatible with --set-upstream"));
+                                       goto skip;
+                               } else {
+                                       source_ref = rm;
+                               }
+                       }
+               }
+               if (source_ref) {
+                       if (!strcmp(source_ref->name, "HEAD") ||
+                           starts_with(source_ref->name, "refs/heads/"))
+                               install_branch_config(0,
+                                                     branch->name,
+                                                     transport->remote->name,
+                                                     source_ref->name);
+                       else if (starts_with(source_ref->name, "refs/remotes/"))
+                               warning(_("not setting upstream for a remote remote-tracking branch"));
+                       else if (starts_with(source_ref->name, "refs/tags/"))
+                               warning(_("not setting upstream for a remote tag"));
+                       else
+                               warning(_("unknown branch type"));
+               } else {
+                       warning(_("no source branch found.\n"
+                               "you need to specify exactly one branch with the --set-upstream option."));
+               }
+       }
+ skip:
        free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
@@ -1510,37 +1557,27 @@ static inline void fetch_one_setup_partial(struct remote *remote)
         * If no prior partial clone/fetch and the current fetch DID NOT
         * request a partial-fetch, do a normal fetch.
         */
-       if (!repository_format_partial_clone && !filter_options.choice)
+       if (!has_promisor_remote() && !filter_options.choice)
                return;
 
        /*
-        * If this is the FIRST partial-fetch request, we enable partial
-        * on this repo and remember the given filter-spec as the default
-        * for subsequent fetches to this remote.
+        * If this is a partial-fetch request, we enable partial on
+        * this repo if not already enabled and remember the given
+        * filter-spec as the default for subsequent fetches to this
+        * remote.
         */
-       if (!repository_format_partial_clone && filter_options.choice) {
+       if (filter_options.choice) {
                partial_clone_register(remote->name, &filter_options);
                return;
        }
 
-       /*
-        * We are currently limited to only ONE promisor remote and only
-        * allow partial-fetches from the promisor remote.
-        */
-       if (strcmp(remote->name, repository_format_partial_clone)) {
-               if (filter_options.choice)
-                       die(_("--filter can only be used with the remote "
-                             "configured in extensions.partialClone"));
-               return;
-       }
-
        /*
         * Do a partial-fetch from the promisor remote using either the
         * explicitly given filter-spec or inherit the filter-spec from
         * the config.
         */
        if (!filter_options.choice)
-               partial_clone_get_default_filter_spec(&filter_options);
+               partial_clone_get_default_filter_spec(&filter_options, remote->name);
        return;
 }
 
@@ -1661,7 +1698,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
 
-       if (filter_options.choice && !repository_format_partial_clone)
+       if (filter_options.choice && !has_promisor_remote())
                die("--filter can only be used when extensions.partialClone is set");
 
        if (all) {
@@ -1695,7 +1732,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        }
 
        if (remote) {
-               if (filter_options.choice || repository_format_partial_clone)
+               if (filter_options.choice || has_promisor_remote())
                        fetch_one_setup_partial(remote);
                result = fetch_one(remote, argc, argv, prune_tags_ok);
        } else {
index 305fb0f45af3bdfc32e15bcc18acdcaa6af205cb..fadb45489f34a760f4c8f6c96c3cc5a5c5c115bd 100644 (file)
@@ -27,6 +27,7 @@
 #include "pack-objects.h"
 #include "blob.h"
 #include "tree.h"
+#include "promisor-remote.h"
 
 #define FAILED_RUN "failed to run %s"
 
@@ -41,7 +42,6 @@ static int aggressive_depth = 50;
 static int aggressive_window = 250;
 static int gc_auto_threshold = 6700;
 static int gc_auto_pack_limit = 50;
-static int gc_write_commit_graph;
 static int detach_auto = 1;
 static timestamp_t gc_log_expire_time;
 static const char *gc_log_expire = "1.day.ago";
@@ -148,7 +148,6 @@ static void gc_config(void)
        git_config_get_int("gc.aggressivedepth", &aggressive_depth);
        git_config_get_int("gc.auto", &gc_auto_threshold);
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
-       git_config_get_bool("gc.writecommitgraph", &gc_write_commit_graph);
        git_config_get_bool("gc.autodetach", &detach_auto);
        git_config_get_expiry("gc.pruneexpire", &prune_expire);
        git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
@@ -661,7 +660,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                        argv_array_push(&prune, prune_expire);
                        if (quiet)
                                argv_array_push(&prune, "--no-progress");
-                       if (repository_format_partial_clone)
+                       if (has_promisor_remote())
                                argv_array_push(&prune,
                                                "--exclude-promisor-objects");
                        if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
@@ -685,11 +684,11 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
                clean_pack_garbage();
        }
 
-       if (gc_write_commit_graph &&
-           write_commit_graph_reachable(get_object_directory(),
-                                        !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
-                                        NULL))
-               return 1;
+       prepare_repo_settings(the_repository);
+       if (the_repository->settings.gc_write_commit_graph == 1)
+               write_commit_graph_reachable(get_object_directory(),
+                                            !quiet && !daemonized ? COMMIT_GRAPH_WRITE_PROGRESS : 0,
+                                            NULL);
 
        if (auto_gc && too_many_loose_objects())
                warning(_("There are too many unreachable loose objects; "
index 0d55f73b0b443b60dccc31096675d40691b3eb56..a23454da6ef9b3a219a48971ef9045772d328155 100644 (file)
@@ -14,7 +14,7 @@
 #include "thread-utils.h"
 #include "packfile.h"
 #include "object-store.h"
-#include "fetch-object.h"
+#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>])";
@@ -1352,7 +1352,7 @@ static void fix_unresolved_deltas(struct hashfile *f)
                sorted_by_pos[i] = &ref_deltas[i];
        QSORT(sorted_by_pos, nr_ref_deltas, delta_pos_compare);
 
-       if (repository_format_partial_clone) {
+       if (has_promisor_remote()) {
                /*
                 * Prefetch the delta bases.
                 */
@@ -1366,8 +1366,8 @@ static void fix_unresolved_deltas(struct hashfile *f)
                        oid_array_append(&to_fetch, &d->oid);
                }
                if (to_fetch.nr)
-                       fetch_objects(repository_format_partial_clone,
-                                     to_fetch.oid, to_fetch.nr);
+                       promisor_remote_get_direct(the_repository,
+                                                  to_fetch.oid, to_fetch.nr);
                oid_array_clear(&to_fetch);
        }
 
index e2ccbc44e204173b09f5ad4b704a31a9e8643bb6..c9746e37b86fa15963ae862ad1aaea5ece92562b 100644 (file)
@@ -81,7 +81,7 @@ static int show_progress = -1;
 static int default_to_upstream = 1;
 static int signoff;
 static const char *sign_commit;
-static int verify_msg = 1;
+static int no_verify;
 
 static struct strategy all_strategy[] = {
        { "recursive",  DEFAULT_TWOHEAD | NO_TRIVIAL },
@@ -287,7 +287,7 @@ static struct option builtin_merge_options[] = {
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
        OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
        OPT_BOOL(0, "signoff", &signoff, N_("add Signed-off-by:")),
-       OPT_BOOL(0, "verify", &verify_msg, N_("verify commit-msg hook")),
+       OPT_BOOL(0, "no-verify", &no_verify, N_("bypass pre-merge-commit and commit-msg hooks")),
        OPT_END()
 };
 
@@ -816,6 +816,18 @@ static void write_merge_heads(struct commit_list *);
 static void prepare_to_commit(struct commit_list *remoteheads)
 {
        struct strbuf msg = STRBUF_INIT;
+       const char *index_file = get_index_file();
+
+       if (!no_verify && run_commit_hook(0 < option_edit, index_file, "pre-merge-commit", NULL))
+               abort_commit(remoteheads, NULL);
+       /*
+        * Re-read the index as pre-merge-commit hook could have updated it,
+        * and write it out as a tree.  We must do this before we invoke
+        * the editor and after we invoke run_status above.
+        */
+       if (find_hook("pre-merge-commit"))
+               discard_cache();
+       read_cache_from(index_file);
        strbuf_addbuf(&msg, &merge_msg);
        if (squash)
                BUG("the control must not reach here under --squash");
@@ -842,7 +854,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
                        abort_commit(remoteheads, NULL);
        }
 
-       if (verify_msg && run_commit_hook(0 < option_edit, get_index_file(),
+       if (!no_verify && run_commit_hook(0 < option_edit, get_index_file(),
                                          "commit-msg",
                                          git_path_merge_msg(the_repository), NULL))
                abort_commit(remoteheads, NULL);
index 76ce9069467e06545652a70040b79316f0a87fb9..c8f51bc65c3621d945ad4a11d9c08929a5250862 100644 (file)
@@ -2342,15 +2342,6 @@ static void find_deltas(struct object_entry **list, unsigned *list_size,
        free(array);
 }
 
-static void try_to_free_from_threads(size_t size)
-{
-       packing_data_lock(&to_pack);
-       release_pack_memory(size);
-       packing_data_unlock(&to_pack);
-}
-
-static try_to_free_t old_try_to_free_routine;
-
 /*
  * The main object list is split into smaller lists, each is handed to
  * one worker.
@@ -2391,12 +2382,10 @@ static void init_threaded_search(void)
        pthread_mutex_init(&cache_mutex, NULL);
        pthread_mutex_init(&progress_mutex, NULL);
        pthread_cond_init(&progress_cond, NULL);
-       old_try_to_free_routine = set_try_to_free_routine(try_to_free_from_threads);
 }
 
 static void cleanup_threaded_search(void)
 {
-       set_try_to_free_routine(old_try_to_free_routine);
        pthread_cond_destroy(&progress_cond);
        pthread_mutex_destroy(&cache_mutex);
        pthread_mutex_destroy(&progress_mutex);
@@ -2715,10 +2704,6 @@ static int git_pack_config(const char *k, const char *v, void *cb)
                use_bitmap_index_default = git_config_bool(k, v);
                return 0;
        }
-       if (!strcmp(k, "pack.usesparse")) {
-               sparse = git_config_bool(k, v);
-               return 0;
-       }
        if (!strcmp(k, "pack.threads")) {
                delta_search_threads = git_config_int(k, v);
                if (delta_search_threads < 0)
@@ -3343,6 +3328,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
        read_replace_refs = 0;
 
        sparse = git_env_bool("GIT_TEST_PACK_SPARSE", 0);
+       prepare_repo_settings(the_repository);
+       if (!sparse && the_repository->settings.pack_use_sparse != -1)
+               sparse = the_repository->settings.pack_use_sparse;
+
        reset_pack_idx_option(&pack_idx_opts);
        git_config(git_pack_config, NULL);
 
index f1eaf6e6edb154417e1b38b4f0cfd9d93ef89130..d25ff13a60f2d08efdc160c359364ab94a5b19ae 100644 (file)
@@ -129,6 +129,7 @@ static char *opt_refmap;
 static char *opt_ipv4;
 static char *opt_ipv6;
 static int opt_show_forced_updates = -1;
+static char *set_upstream;
 
 static struct option pull_options[] = {
        /* Shared options */
@@ -243,6 +244,9 @@ static struct option pull_options[] = {
                PARSE_OPT_NOARG),
        OPT_BOOL(0, "show-forced-updates", &opt_show_forced_updates,
                 N_("check for forced-updates on all updated branches")),
+       OPT_PASSTHRU(0, "set-upstream", &set_upstream, NULL,
+               N_("set upstream for git pull/fetch"),
+               PARSE_OPT_NOARG),
 
        OPT_END()
 };
@@ -556,6 +560,8 @@ static int run_fetch(const char *repo, const char **refspecs)
                argv_array_push(&args, "--show-forced-updates");
        else if (opt_show_forced_updates == 0)
                argv_array_push(&args, "--no-show-forced-updates");
+       if (set_upstream)
+               argv_array_push(&args, set_upstream);
 
        if (repo) {
                argv_array_push(&args, repo);
index 670096c065f5f5fa8cb57c642cd759111b010988..e8319d594639dcd85bb47b5f1ece1a4fdd5c1a4c 100644 (file)
@@ -62,7 +62,7 @@ struct rebase_options {
        const char *onto_name;
        const char *revisions;
        const char *switch_to;
-       int root;
+       int root, root_with_onto;
        struct object_id *squash_onto;
        struct commit *restrict_revision;
        int dont_finish_rebase;
@@ -374,6 +374,7 @@ static int run_rebase_interactive(struct rebase_options *opts,
        flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
        flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
        flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
+       flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
        flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
 
        switch (command) {
@@ -1833,15 +1834,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                              "'--reschedule-failed-exec'"));
        }
 
-       if (options.rebase_merges) {
-               if (strategy_options.nr)
-                       die(_("cannot combine '--rebase-merges' with "
-                             "'--strategy-option'"));
-               if (options.strategy)
-                       die(_("cannot combine '--rebase-merges' with "
-                             "'--strategy'"));
-       }
-
        if (!options.root) {
                if (argc < 1) {
                        struct branch *branch;
@@ -1872,7 +1864,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
                        options.squash_onto = &squash_onto;
                        options.onto_name = squash_onto_name =
                                xstrdup(oid_to_hex(&squash_onto));
-               }
+               } else
+                       options.root_with_onto = 1;
+
                options.upstream_name = NULL;
                options.upstream = NULL;
                if (argc > 1)
index 632c0c0a79422a229d52c83665331501e8c54e29..3b3dd1437299a53a9031572820c12f3269922695 100644 (file)
@@ -11,6 +11,7 @@
 #include "midx.h"
 #include "packfile.h"
 #include "object-store.h"
+#include "promisor-remote.h"
 
 static int delta_base_offset = 1;
 static int pack_kept_objects = -1;
@@ -361,7 +362,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
        argv_array_push(&cmd.args, "--all");
        argv_array_push(&cmd.args, "--reflog");
        argv_array_push(&cmd.args, "--indexed-objects");
-       if (repository_format_partial_clone)
+       if (has_promisor_remote())
                argv_array_push(&cmd.args, "--exclude-promisor-objects");
        if (write_bitmaps > 0)
                argv_array_push(&cmd.args, "--write-bitmap-index");
index 301ccb970bb36340bc6b4a136a3029811f252aa2..b8dc2e1fba6ce4c7763a55924f34d86796d982a2 100644 (file)
@@ -473,8 +473,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                                die(_("object filtering requires --objects"));
                        if (filter_options.choice == LOFC_SPARSE_OID &&
                            !filter_options.sparse_oid_value)
-                               die(_("invalid sparse value '%s'"),
-                                   filter_options.filter_spec);
+                               die(
+                                       _("invalid sparse value '%s'"),
+                                       list_objects_filter_spec(
+                                               &filter_options));
                        continue;
                }
                if (!strcmp(arg, ("--no-" CL_ARG__FILTER))) {
index dff2f4b837208deebeb34f4af687c18df7dbfe76..49302d98c55d19651cacef7b36197de65d805e50 100644 (file)
@@ -966,6 +966,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        struct parse_opt_ctx_t ctx;
        strbuf_getline_fn getline_fn;
        int parseopt_state = PARSE_OPT_UNKNOWN;
+       struct repository *r = the_repository;
        struct option options[] = {
                OPT_BIT('q', NULL, &refresh_args.flags,
                        N_("continue refresh even when index needs update"),
@@ -1180,11 +1181,12 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                remove_split_index(&the_index);
        }
 
+       prepare_repo_settings(r);
        switch (untracked_cache) {
        case UC_UNSPECIFIED:
                break;
        case UC_DISABLE:
-               if (git_config_get_untracked_cache() == 1)
+               if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
                        warning(_("core.untrackedCache is set to true; "
                                  "remove or change it, if you really want to "
                                  "disable the untracked cache"));
@@ -1196,7 +1198,7 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
                return !test_if_untracked_cache_is_supported();
        case UC_ENABLE:
        case UC_FORCE:
-               if (git_config_get_untracked_cache() == 0)
+               if (r->settings.core_untracked_cache == UNTRACKED_CACHE_REMOVE)
                        warning(_("core.untrackedCache is set to false; "
                                  "remove or change it, if you really want to "
                                  "enable the untracked cache"));
index c22161f987152ea18715b00815e987847786c5ff..0e5724fad752e9157dbcf1caba0438b42386ee3d 100644 (file)
@@ -5,6 +5,7 @@
 #include "cache-tree.h"
 #include "object-store.h"
 #include "replace-object.h"
+#include "promisor-remote.h"
 
 #ifndef DEBUG_CACHE_TREE
 #define DEBUG_CACHE_TREE 0
@@ -357,7 +358,7 @@ static int update_one(struct cache_tree *it,
                }
 
                ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
-                       (repository_format_partial_clone &&
+                       (has_promisor_remote() &&
                         ce_skip_worktree(ce));
                if (is_null_oid(oid) ||
                    (!ce_missing_ok && !has_object_file(oid))) {
diff --git a/cache.h b/cache.h
index b1da1ab08faad3da19657a9a5dcf5f2592c2127c..5624e6c02d5e72ab2975bb3219329e4580f96f55 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -636,6 +636,9 @@ int daemonize(void);
  * at least 'nr' entries; the number of entries currently allocated
  * is 'alloc', using the standard growing factor alloc_nr() macro.
  *
+ * Consider using ALLOC_GROW_BY instead of ALLOC_GROW as it has some
+ * added niceties.
+ *
  * DO NOT USE any expression with side-effect for 'x', 'nr', or 'alloc'.
  */
 #define ALLOC_GROW(x, nr, alloc) \
@@ -649,6 +652,25 @@ int daemonize(void);
                } \
        } while (0)
 
+/*
+ * Similar to ALLOC_GROW but handles updating of the nr value and
+ * zeroing the bytes of the newly-grown array elements.
+ *
+ * DO NOT USE any expression with side-effect for any of the
+ * arguments.
+ */
+#define ALLOC_GROW_BY(x, nr, increase, alloc) \
+       do { \
+               if (increase) { \
+                       size_t new_nr = nr + (increase); \
+                       if (new_nr < nr) \
+                               BUG("negative growth in ALLOC_GROW_BY"); \
+                       ALLOC_GROW(x, new_nr, alloc); \
+                       memset((x) + nr, 0, sizeof(*(x)) * (increase)); \
+                       nr = new_nr; \
+               } \
+       } while (0)
+
 /* Initialize and use the cache information */
 struct lock_file;
 void preload_index(struct index_state *index,
@@ -937,8 +959,6 @@ extern int grafts_replace_parents;
 #define GIT_REPO_VERSION 0
 #define GIT_REPO_VERSION_READ 1
 extern int repository_format_precious_objects;
-extern char *repository_format_partial_clone;
-extern const char *core_partial_clone_filter_default;
 extern int repository_format_worktree_config;
 
 /*
index f2888c203b677a8a0091f7f63129efe282899172..9b02d2c42657b4cef247fee6aa4240dead0ae776 100644 (file)
@@ -467,7 +467,6 @@ static void prepare_commit_graph_one(struct repository *r, const char *obj_dir)
 static int prepare_commit_graph(struct repository *r)
 {
        struct object_directory *odb;
-       int config_value;
 
        if (git_env_bool(GIT_TEST_COMMIT_GRAPH_DIE_ON_LOAD, 0))
                die("dying as requested by the '%s' variable on commit-graph load!",
@@ -477,9 +476,10 @@ static int prepare_commit_graph(struct repository *r)
                return !!r->objects->commit_graph;
        r->objects->commit_graph_attempted = 1;
 
+       prepare_repo_settings(r);
+
        if (!git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
-           (repo_config_get_bool(r, "core.commitgraph", &config_value) ||
-           !config_value))
+           r->settings.core_commit_graph != 1)
                /*
                 * This repository is not configured to use commit graphs, so
                 * do not load one. (But report commit_graph_attempted anyway
index 582a7b18869fb80c94b3a0057e4cb9ced4c6f1c2..71e21dd20a3b141bed0d37512cdc9196321dc315 100644 (file)
@@ -39,16 +39,16 @@ int main(int argc, const char **argv)
 
        git_resolve_executable_dir(argv[0]);
 
-       trace2_initialize();
-       trace2_cmd_start(argv);
-       trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
-
        git_setup_gettext();
 
        initialize_the_repository();
 
        attr_start();
 
+       trace2_initialize();
+       trace2_cmd_start(argv);
+       trace2_collect_process_info(TRACE2_PROCESS_INFO_STARTUP);
+
        result = cmd_main(argc, argv);
 
        trace2_cmd_exit(result);
index 738f0a826a51b850fcc93f0cadbf707c7eb0b492..a3b1e9e3bb099ef1b38e03177a11c9270e123800 100644 (file)
@@ -1265,7 +1265,7 @@ static wchar_t *make_environment_block(char **deltaenv)
                }
 
                ALLOC_ARRAY(result, size);
-               memcpy(result, wenv, size * sizeof(*wenv));
+               COPY_ARRAY(result, wenv, size);
                FreeEnvironmentStringsW(wenv);
                return result;
        }
@@ -1309,7 +1309,7 @@ static wchar_t *make_environment_block(char **deltaenv)
                        continue;
 
                size = wcslen(array[i]) + 1;
-               memcpy(p, array[i], size * sizeof(*p));
+               COPY_ARRAY(p, array[i], size);
                p += size;
        }
        *p = L'\0';
index a03e40e6e2a6cba13ed1fed2d4d690443fcd40a8..9ad204c57ce21d22550f7e28bc23bcbb0fa50e48 100644 (file)
@@ -11,7 +11,7 @@ typedef _sigset_t sigset_t;
 #undef _POSIX_THREAD_SAFE_FUNCTIONS
 #endif
 
-extern int mingw_core_config(const char *var, const char *value, void *cb);
+int mingw_core_config(const char *var, const char *value, void *cb);
 #define platform_core_config mingw_core_config
 
 /*
@@ -443,7 +443,7 @@ static inline void convert_slashes(char *path)
                        *path = '/';
 }
 #define PATH_SEP ';'
-extern char *mingw_query_user_email(void);
+char *mingw_query_user_email(void);
 #define query_user_email mingw_query_user_email
 #if !defined(__MINGW64_VERSION_MAJOR) && (!defined(_MSC_VER) || _MSC_VER < 1800)
 #define PRIuMAX "I64u"
@@ -580,4 +580,4 @@ int main(int argc, const char **argv);
 /*
  * Used by Pthread API implementation for Windows
  */
-extern int err_win_to_posix(DWORD winerr);
+int err_win_to_posix(DWORD winerr);
index c6cb8dd2190062b3c3db2ac93a996c859a2e1145..f1cfe73de9fc7db2f6b230ff9aab1b08069cda51 100644 (file)
@@ -50,7 +50,7 @@ typedef struct {
        DWORD tid;
 } pthread_t;
 
-extern int pthread_create(pthread_t *thread, const void *unused,
+int pthread_create(pthread_t *thread, const void *unused,
                          void *(*start_routine)(void*), void *arg);
 
 /*
@@ -59,10 +59,10 @@ extern int pthread_create(pthread_t *thread, const void *unused,
  */
 #define pthread_join(a, b) win32_pthread_join(&(a), (b))
 
-extern int win32_pthread_join(pthread_t *thread, void **value_ptr);
+int win32_pthread_join(pthread_t *thread, void **value_ptr);
 
 #define pthread_equal(t1, t2) ((t1).tid == (t2).tid)
-extern pthread_t pthread_self(void);
+pthread_t pthread_self(void);
 
 static inline void NORETURN pthread_exit(void *ret)
 {
index 3900e4947be92b916ed9b531eb455e8f856105dc..743e4570ee38cb6fffe7be7ed06ecdb590481016 100644 (file)
--- a/config.c
+++ b/config.c
@@ -275,7 +275,7 @@ static int include_by_branch(const char *cond, size_t cond_len)
        int flags;
        int ret;
        struct strbuf pattern = STRBUF_INIT;
-       const char *refname = !the_repository || !the_repository->gitdir ?
+       const char *refname = !the_repository->gitdir ?
                NULL : resolve_ref_unsafe("HEAD", 0, NULL, &flags);
        const char *shortname;
 
@@ -1379,11 +1379,6 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
                return 0;
        }
 
-       if (!strcmp(var, "core.partialclonefilter")) {
-               return git_config_string(&core_partial_clone_filter_default,
-                                        var, value);
-       }
-
        if (!strcmp(var, "core.usereplacerefs")) {
                read_replace_refs = git_config_bool(var, value);
                return 0;
@@ -2288,30 +2283,6 @@ int git_config_get_expiry_in_days(const char *key, timestamp_t *expiry, timestam
        return -1; /* thing exists but cannot be parsed */
 }
 
-int git_config_get_untracked_cache(void)
-{
-       int val = -1;
-       const char *v;
-
-       /* Hack for test programs like test-dump-untracked-cache */
-       if (ignore_untracked_cache_config)
-               return -1;
-
-       if (!git_config_get_maybe_bool("core.untrackedcache", &val))
-               return val;
-
-       if (!git_config_get_value("core.untrackedcache", &v)) {
-               if (!strcasecmp(v, "keep"))
-                       return -1;
-
-               error(_("unknown core.untrackedCache value '%s'; "
-                       "using 'keep' default value"), v);
-               return -1;
-       }
-
-       return -1; /* default value */
-}
-
 int git_config_get_split_index(void)
 {
        int val;
index cd9b324afa5a33be7eced6a420061905d52c211f..971db009b327e63cf71c82b496d70604cecf6415 100644 (file)
@@ -5,6 +5,7 @@
 #include "connected.h"
 #include "transport.h"
 #include "packfile.h"
+#include "promisor-remote.h"
 
 /*
  * If we feed all the commits we want to verify to this command
@@ -73,7 +74,7 @@ int check_connected(oid_iterate_fn fn, void *cb_data,
        argv_array_push(&rev_list.args,"rev-list");
        argv_array_push(&rev_list.args, "--objects");
        argv_array_push(&rev_list.args, "--stdin");
-       if (repository_format_partial_clone)
+       if (has_promisor_remote())
                argv_array_push(&rev_list.args, "--exclude-promisor-objects");
        if (!opt->is_deepening_fetch) {
                argv_array_push(&rev_list.args, "--not");
index e087c4bf0085add8e968e128db6b667acbc80320..ce7ff0a96d345fd6fb5c957d4d404f40598eb9c2 100644 (file)
@@ -340,7 +340,7 @@ __gitcomp ()
                        c="$c${4-}"
                        if [[ $c == "$cur_"* ]]; then
                                case $c in
-                               --*=*|*.) ;;
+                               --*=|*.) ;;
                                *) c="$c " ;;
                                esac
                                COMPREPLY[i++]="${2-}$c"
@@ -360,7 +360,7 @@ __gitcomp ()
                        c="$c${4-}"
                        if [[ $c == "$cur_"* ]]; then
                                case $c in
-                               --*=*|*.) ;;
+                               *=|*.) ;;
                                *) c="$c " ;;
                                esac
                                COMPREPLY[i++]="${2-}$c"
@@ -524,7 +524,7 @@ __git_index_files ()
                        # Even when a directory name itself does not contain
                        # any special characters, it will still be quoted if
                        # any of its (stripped) trailing path components do.
-                       # Because of this we may have seen the same direcory
+                       # Because of this we may have seen the same directory
                        # both quoted and unquoted.
                        if (p in paths)
                                # We have seen the same directory unquoted,
@@ -1399,7 +1399,18 @@ _git_clean ()
 
 _git_clone ()
 {
+       case "$prev" in
+       -c|--config)
+               __git_complete_config_variable_name_and_value
+               return
+               ;;
+       esac
        case "$cur" in
+       --config=*)
+               __git_complete_config_variable_name_and_value \
+                       --cur="${cur##--config=}"
+               return
+               ;;
        --*)
                __gitcomp_builtin clone
                return
@@ -2225,181 +2236,282 @@ __git_config_vars=
 __git_compute_config_vars ()
 {
        test -n "$__git_config_vars" ||
-       __git_config_vars="$(git help --config-for-completion | sort | uniq)"
+       __git_config_vars="$(git help --config-for-completion | sort -u)"
 }
 
-_git_config ()
+# Completes possible values of various configuration variables.
+#
+# Usage: __git_complete_config_variable_value [<option>]...
+# --varname=<word>: The name of the configuration variable whose value is
+#                   to be completed.  Defaults to the previous word on the
+#                   command line.
+# --cur=<word>: The current value to be completed.  Defaults to the current
+#               word to be completed.
+__git_complete_config_variable_value ()
 {
-       local varname
+       local varname="$prev" cur_="$cur"
+
+       while test $# != 0; do
+               case "$1" in
+               --varname=*)    varname="${1##--varname=}" ;;
+               --cur=*)        cur_="${1##--cur=}" ;;
+               *)              return 1 ;;
+               esac
+               shift
+       done
 
        if [ "${BASH_VERSINFO[0]:-0}" -ge 4 ]; then
-               varname="${prev,,}"
+               varname="${varname,,}"
        else
-               varname="$(echo "$prev" |tr A-Z a-z)"
+               varname="$(echo "$varname" |tr A-Z a-z)"
        fi
 
        case "$varname" in
        branch.*.remote|branch.*.pushremote)
-               __gitcomp_nl "$(__git_remotes)"
+               __gitcomp_nl "$(__git_remotes)" "" "$cur_"
                return
                ;;
        branch.*.merge)
-               __git_complete_refs
+               __git_complete_refs --cur="$cur_"
                return
                ;;
        branch.*.rebase)
-               __gitcomp "false true merges preserve interactive"
+               __gitcomp "false true merges preserve interactive" "" "$cur_"
                return
                ;;
        remote.pushdefault)
-               __gitcomp_nl "$(__git_remotes)"
+               __gitcomp_nl "$(__git_remotes)" "" "$cur_"
                return
                ;;
        remote.*.fetch)
-               local remote="${prev#remote.}"
+               local remote="${varname#remote.}"
                remote="${remote%.fetch}"
-               if [ -z "$cur" ]; then
+               if [ -z "$cur_" ]; then
                        __gitcomp_nl "refs/heads/" "" "" ""
                        return
                fi
-               __gitcomp_nl "$(__git_refs_remotes "$remote")"
+               __gitcomp_nl "$(__git_refs_remotes "$remote")" "" "$cur_"
                return
                ;;
        remote.*.push)
-               local remote="${prev#remote.}"
+               local remote="${varname#remote.}"
                remote="${remote%.push}"
                __gitcomp_nl "$(__git for-each-ref \
-                       --format='%(refname):%(refname)' refs/heads)"
+                       --format='%(refname):%(refname)' refs/heads)" "" "$cur_"
                return
                ;;
        pull.twohead|pull.octopus)
                __git_compute_merge_strategies
-               __gitcomp "$__git_merge_strategies"
-               return
-               ;;
-       color.branch|color.diff|color.interactive|\
-       color.showbranch|color.status|color.ui)
-               __gitcomp "always never auto"
+               __gitcomp "$__git_merge_strategies" "" "$cur_"
                return
                ;;
        color.pager)
-               __gitcomp "false true"
+               __gitcomp "false true" "" "$cur_"
                return
                ;;
        color.*.*)
                __gitcomp "
                        normal black red green yellow blue magenta cyan white
                        bold dim ul blink reverse
-                       "
+                       " "" "$cur_"
+               return
+               ;;
+       color.*)
+               __gitcomp "false true always never auto" "" "$cur_"
                return
                ;;
        diff.submodule)
-               __gitcomp "$__git_diff_submodule_formats"
+               __gitcomp "$__git_diff_submodule_formats" "" "$cur_"
                return
                ;;
        help.format)
-               __gitcomp "man info web html"
+               __gitcomp "man info web html" "" "$cur_"
                return
                ;;
        log.date)
-               __gitcomp "$__git_log_date_formats"
+               __gitcomp "$__git_log_date_formats" "" "$cur_"
                return
                ;;
        sendemail.aliasfiletype)
-               __gitcomp "mutt mailrc pine elm gnus"
+               __gitcomp "mutt mailrc pine elm gnus" "" "$cur_"
                return
                ;;
        sendemail.confirm)
-               __gitcomp "$__git_send_email_confirm_options"
+               __gitcomp "$__git_send_email_confirm_options" "" "$cur_"
                return
                ;;
        sendemail.suppresscc)
-               __gitcomp "$__git_send_email_suppresscc_options"
+               __gitcomp "$__git_send_email_suppresscc_options" "" "$cur_"
                return
                ;;
        sendemail.transferencoding)
-               __gitcomp "7bit 8bit quoted-printable base64"
-               return
-               ;;
-       --get|--get-all|--unset|--unset-all)
-               __gitcomp_nl "$(__git_config_get_set_variables)"
+               __gitcomp "7bit 8bit quoted-printable base64" "" "$cur_"
                return
                ;;
        *.*)
                return
                ;;
        esac
-       case "$cur" in
-       --*)
-               __gitcomp_builtin config
-               return
-               ;;
+}
+
+# Completes configuration sections, subsections, variable names.
+#
+# Usage: __git_complete_config_variable_name [<option>]...
+# --cur=<word>: The current configuration section/variable name to be
+#               completed.  Defaults to the current word to be completed.
+# --sfx=<suffix>: A suffix to be appended to each fully completed
+#                 configuration variable name (but not to sections or
+#                 subsections) instead of the default space.
+__git_complete_config_variable_name ()
+{
+       local cur_="$cur" sfx
+
+       while test $# != 0; do
+               case "$1" in
+               --cur=*)        cur_="${1##--cur=}" ;;
+               --sfx=*)        sfx="${1##--sfx=}" ;;
+               *)              return 1 ;;
+               esac
+               shift
+       done
+
+       case "$cur_" in
        branch.*.*)
-               local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_"
+               local pfx="${cur_%.*}."
+               cur_="${cur_##*.}"
+               __gitcomp "remote pushRemote merge mergeOptions rebase" "$pfx" "$cur_" "$sfx"
                return
                ;;
        branch.*)
-               local pfx="${cur%.*}." cur_="${cur#*.}"
+               local pfx="${cur%.*}."
+               cur_="${cur#*.}"
                __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
-               __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_"
+               __gitcomp_nl_append $'autoSetupMerge\nautoSetupRebase\n' "$pfx" "$cur_" "$sfx"
                return
                ;;
        guitool.*.*)
-               local pfx="${cur%.*}." cur_="${cur##*.}"
+               local pfx="${cur_%.*}."
+               cur_="${cur_##*.}"
                __gitcomp "
                        argPrompt cmd confirm needsFile noConsole noRescan
                        prompt revPrompt revUnmerged title
-                       " "$pfx" "$cur_"
+                       " "$pfx" "$cur_" "$sfx"
                return
                ;;
        difftool.*.*)
-               local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "cmd path" "$pfx" "$cur_"
+               local pfx="${cur_%.*}."
+               cur_="${cur_##*.}"
+               __gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
                return
                ;;
        man.*.*)
-               local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "cmd path" "$pfx" "$cur_"
+               local pfx="${cur_%.*}."
+               cur_="${cur_##*.}"
+               __gitcomp "cmd path" "$pfx" "$cur_" "$sfx"
                return
                ;;
        mergetool.*.*)
-               local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "cmd path trustExitCode" "$pfx" "$cur_"
+               local pfx="${cur_%.*}."
+               cur_="${cur_##*.}"
+               __gitcomp "cmd path trustExitCode" "$pfx" "$cur_" "$sfx"
                return
                ;;
        pager.*)
-               local pfx="${cur%.*}." cur_="${cur#*.}"
+               local pfx="${cur_%.*}."
+               cur_="${cur_#*.}"
                __git_compute_all_commands
-               __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_"
+               __gitcomp_nl "$__git_all_commands" "$pfx" "$cur_" "$sfx"
                return
                ;;
        remote.*.*)
-               local pfx="${cur%.*}." cur_="${cur##*.}"
+               local pfx="${cur_%.*}."
+               cur_="${cur_##*.}"
                __gitcomp "
                        url proxy fetch push mirror skipDefaultUpdate
                        receivepack uploadpack tagOpt pushurl
-                       " "$pfx" "$cur_"
+                       " "$pfx" "$cur_" "$sfx"
                return
                ;;
        remote.*)
-               local pfx="${cur%.*}." cur_="${cur#*.}"
+               local pfx="${cur_%.*}."
+               cur_="${cur_#*.}"
                __gitcomp_nl "$(__git_remotes)" "$pfx" "$cur_" "."
-               __gitcomp_nl_append "pushDefault" "$pfx" "$cur_"
+               __gitcomp_nl_append "pushDefault" "$pfx" "$cur_" "$sfx"
                return
                ;;
        url.*.*)
-               local pfx="${cur%.*}." cur_="${cur##*.}"
-               __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_"
+               local pfx="${cur_%.*}."
+               cur_="${cur_##*.}"
+               __gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur_" "$sfx"
                return
                ;;
        *.*)
                __git_compute_config_vars
-               __gitcomp "$__git_config_vars"
+               __gitcomp "$__git_config_vars" "" "$cur_" "$sfx"
                ;;
        *)
                __git_compute_config_vars
-               __gitcomp "$(echo "$__git_config_vars" | sed 's/\.[^ ]*/./g')"
+               __gitcomp "$(echo "$__git_config_vars" |
+                               awk -F . '{
+                                       sections[$1] = 1
+                               }
+                               END {
+                                       for (s in sections)
+                                               print s "."
+                               }
+                               ')" "" "$cur_"
+               ;;
+       esac
+}
+
+# Completes '='-separated configuration sections/variable names and values
+# for 'git -c section.name=value'.
+#
+# Usage: __git_complete_config_variable_name_and_value [<option>]...
+# --cur=<word>: The current configuration section/variable name/value to be
+#               completed. Defaults to the current word to be completed.
+__git_complete_config_variable_name_and_value ()
+{
+       local cur_="$cur"
+
+       while test $# != 0; do
+               case "$1" in
+               --cur=*)        cur_="${1##--cur=}" ;;
+               *)              return 1 ;;
+               esac
+               shift
+       done
+
+       case "$cur_" in
+       *=*)
+               __git_complete_config_variable_value \
+                       --varname="${cur_%%=*}" --cur="${cur_#*=}"
+               ;;
+       *)
+               __git_complete_config_variable_name --cur="$cur_" --sfx='='
+               ;;
+       esac
+}
+
+_git_config ()
+{
+       case "$prev" in
+       --get|--get-all|--unset|--unset-all)
+               __gitcomp_nl "$(__git_config_get_set_variables)"
+               return
+               ;;
+       *.*)
+               __git_complete_config_variable_value
+               return
+               ;;
+       esac
+       case "$cur" in
+       --*)
+               __gitcomp_builtin config
+               ;;
+       *)
+               __git_complete_config_variable_name
+               ;;
        esac
 }
 
@@ -2956,7 +3068,11 @@ __git_main ()
                        # Bash filename completion
                        return
                        ;;
-               -c|--namespace)
+               -c)
+                       __git_complete_config_variable_name_and_value
+                       return
+                       ;;
+               --namespace)
                        # we don't support completing these options' arguments
                        return
                        ;;
index 94ff8376492257782a1af5b3d2851ee8724c2edd..deb6f71b2d164fc87dab3b741da34dde98c51374 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -8,6 +8,7 @@
 #include "pkt-line.h"
 #include "sub-process.h"
 #include "utf8.h"
+#include "ll-merge.h"
 
 /*
  * convert.c - convert a file when checking it out and checking it in.
@@ -1293,10 +1294,11 @@ struct conv_attrs {
        const char *working_tree_encoding; /* Supported encoding or default encoding if NULL */
 };
 
+static struct attr_check *check;
+
 static void convert_attrs(const struct index_state *istate,
                          struct conv_attrs *ca, const char *path)
 {
-       static struct attr_check *check;
        struct attr_check_item *ccheck = NULL;
 
        if (!check) {
@@ -1339,6 +1341,23 @@ static void convert_attrs(const struct index_state *istate,
                ca->crlf_action = CRLF_AUTO_INPUT;
 }
 
+void reset_parsed_attributes(void)
+{
+       struct convert_driver *drv, *next;
+
+       attr_check_free(check);
+       check = NULL;
+       reset_merge_attributes();
+
+       for (drv = user_convert; drv; drv = next) {
+               next = drv->next;
+               free((void *)drv->name);
+               free(drv);
+       }
+       user_convert = NULL;
+       user_convert_tail = NULL;
+}
+
 int would_convert_to_git_filter_fd(const struct index_state *istate, const char *path)
 {
        struct conv_attrs ca;
index 831559f10d4442b35a43c642b0fd6c203d996f3c..3710969d43d6a16c6eab2e8302f962aa466ccd9f 100644 (file)
--- a/convert.h
+++ b/convert.h
@@ -94,6 +94,12 @@ void convert_to_git_filter_fd(const struct index_state *istate,
 int would_convert_to_git_filter_fd(const struct index_state *istate,
                                   const char *path);
 
+/*
+ * Reset the internal list of attributes used by convert_to_git and
+ * convert_to_working_tree.
+ */
+void reset_parsed_attributes(void);
+
 /*****************************************************************
  *
  * Streaming conversion support
index ac295420dd0d03d1b31922f9b7c16a83e987e6a3..c010497cb21db3c2bc921caf1b34be3e60ff5570 100644 (file)
@@ -72,15 +72,16 @@ static void store_credential_file(const char *fn, struct credential *c)
        struct strbuf buf = STRBUF_INIT;
 
        strbuf_addf(&buf, "%s://", c->protocol);
-       strbuf_addstr_urlencode(&buf, c->username, 1);
+       strbuf_addstr_urlencode(&buf, c->username, is_rfc3986_unreserved);
        strbuf_addch(&buf, ':');
-       strbuf_addstr_urlencode(&buf, c->password, 1);
+       strbuf_addstr_urlencode(&buf, c->password, is_rfc3986_unreserved);
        strbuf_addch(&buf, '@');
        if (c->host)
-               strbuf_addstr_urlencode(&buf, c->host, 1);
+               strbuf_addstr_urlencode(&buf, c->host, is_rfc3986_unreserved);
        if (c->path) {
                strbuf_addch(&buf, '/');
-               strbuf_addstr_urlencode(&buf, c->path, 0);
+               strbuf_addstr_urlencode(&buf, c->path,
+                                       is_rfc3986_reserved_or_unreserved);
        }
 
        rewrite_credential_file(fn, c, &buf);
diff --git a/diff.c b/diff.c
index efe42b341ae15ebceb1e216518ab3a542be361dc..6db6927369e5c18ae3cb7ea27a0ebce37fd2b357 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -25,7 +25,7 @@
 #include "packfile.h"
 #include "parse-options.h"
 #include "help.h"
-#include "fetch-object.h"
+#include "promisor-remote.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -6512,6 +6512,7 @@ static void add_if_missing(struct repository *r,
                           const struct diff_filespec *filespec)
 {
        if (filespec && filespec->oid_valid &&
+           !S_ISGITLINK(filespec->mode) &&
            oid_object_info_extended(r, &filespec->oid, NULL,
                                     OBJECT_INFO_FOR_PREFETCH))
                oid_array_append(to_fetch, &filespec->oid);
@@ -6519,8 +6520,7 @@ static void add_if_missing(struct repository *r,
 
 void diffcore_std(struct diff_options *options)
 {
-       if (options->repo == the_repository &&
-           repository_format_partial_clone) {
+       if (options->repo == the_repository && has_promisor_remote()) {
                /*
                 * Prefetch the diff pairs that are about to be flushed.
                 */
@@ -6537,8 +6537,8 @@ void diffcore_std(struct diff_options *options)
                        /*
                         * NEEDSWORK: Consider deduplicating the OIDs sent.
                         */
-                       fetch_objects(repository_format_partial_clone,
-                                     to_fetch.oid, to_fetch.nr);
+                       promisor_remote_get_direct(options->repo,
+                                                  to_fetch.oid, to_fetch.nr);
                oid_array_clear(&to_fetch);
        }
 
index 89af47cb8504903b90460d5faa91be9f61bd2fc3..efa072680a2bca0d317b66554a325b73c227c866 100644 (file)
@@ -31,8 +31,6 @@ int warn_ambiguous_refs = 1;
 int warn_on_object_refname_ambiguity = 1;
 int ref_paranoia = -1;
 int repository_format_precious_objects;
-char *repository_format_partial_clone;
-const char *core_partial_clone_filter_default;
 int repository_format_worktree_config;
 const char *git_commit_encoding;
 const char *git_log_output_encoding;
index d6d685cba012d6765b3998fde14b5c40fff82a96..0a1357dc9d55b34992ff5a6f65e3bdf77375465e 100644 (file)
@@ -2,19 +2,20 @@
 #include "fetch-negotiator.h"
 #include "negotiator/default.h"
 #include "negotiator/skipping.h"
+#include "repository.h"
 
-void fetch_negotiator_init(struct fetch_negotiator *negotiator,
-                          const char *algorithm)
+void fetch_negotiator_init(struct repository *r,
+                          struct fetch_negotiator *negotiator)
 {
-       if (algorithm) {
-               if (!strcmp(algorithm, "skipping")) {
-                       skipping_negotiator_init(negotiator);
-                       return;
-               } else if (!strcmp(algorithm, "default")) {
-                       /* Fall through to default initialization */
-               } else {
-                       die("unknown fetch negotiation algorithm '%s'", algorithm);
-               }
+       prepare_repo_settings(r);
+       switch(r->settings.fetch_negotiation_algorithm) {
+       case FETCH_NEGOTIATION_SKIPPING:
+               skipping_negotiator_init(negotiator);
+               return;
+
+       case FETCH_NEGOTIATION_DEFAULT:
+       default:
+               default_negotiator_init(negotiator);
+               return;
        }
-       default_negotiator_init(negotiator);
 }
index 9e3967ce6626be459ad7d8a0590240b06e7056e3..ea78868504bdcff0121c40cd40b6857df1680cfc 100644 (file)
@@ -2,6 +2,7 @@
 #define FETCH_NEGOTIATOR_H
 
 struct commit;
+struct repository;
 
 /*
  * An object that supplies the information needed to negotiate the contents of
@@ -52,7 +53,7 @@ struct fetch_negotiator {
        void *data;
 };
 
-void fetch_negotiator_init(struct fetch_negotiator *negotiator,
-                          const char *algorithm);
+void fetch_negotiator_init(struct repository *r,
+                          struct fetch_negotiator *negotiator);
 
 #endif
diff --git a/fetch-object.c b/fetch-object.c
deleted file mode 100644 (file)
index 4266548..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-#include "cache.h"
-#include "packfile.h"
-#include "pkt-line.h"
-#include "strbuf.h"
-#include "transport.h"
-#include "fetch-object.h"
-
-static void fetch_refs(const char *remote_name, struct ref *ref)
-{
-       struct remote *remote;
-       struct transport *transport;
-       int original_fetch_if_missing = fetch_if_missing;
-
-       fetch_if_missing = 0;
-       remote = remote_get(remote_name);
-       if (!remote->url[0])
-               die(_("Remote with no URL"));
-       transport = transport_get(remote, remote->url[0]);
-
-       transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
-       transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
-       transport_fetch_refs(transport, ref);
-       fetch_if_missing = original_fetch_if_missing;
-}
-
-void fetch_objects(const char *remote_name, const struct object_id *oids,
-                  int oid_nr)
-{
-       struct ref *ref = NULL;
-       int i;
-
-       for (i = 0; i < oid_nr; i++) {
-               struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i]));
-               oidcpy(&new_ref->old_oid, &oids[i]);
-               new_ref->exact_oid = 1;
-               new_ref->next = ref;
-               ref = new_ref;
-       }
-       fetch_refs(remote_name, ref);
-}
diff --git a/fetch-object.h b/fetch-object.h
deleted file mode 100644 (file)
index d6444ca..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#ifndef FETCH_OBJECT_H
-#define FETCH_OBJECT_H
-
-struct object_id;
-
-void fetch_objects(const char *remote_name, const struct object_id *oids,
-                  int oid_nr);
-
-#endif
index 65be043f2afafdb37b4ce4d6fe8fbd3ac0eb2eaf..6ccc6294ea7b7fe2da9d2306acdcf40031a4383f 100644 (file)
@@ -36,7 +36,6 @@ static int agent_supported;
 static int server_supports_filtering;
 static struct lock_file shallow_lock;
 static const char *alternate_shallow_file;
-static char *negotiation_algorithm;
 static struct strbuf fsck_msg_types = STRBUF_INIT;
 
 /* Remember to update object flag allocation in object.h */
@@ -339,12 +338,9 @@ static int find_common(struct fetch_negotiator *negotiator,
                }
        }
        if (server_supports_filtering && args->filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(&args->filter_options,
-                                               &expanded_filter_spec);
-               packet_buf_write(&req_buf, "filter %s",
-                                expanded_filter_spec.buf);
-               strbuf_release(&expanded_filter_spec);
+               const char *spec =
+                       expand_list_objects_filter_spec(&args->filter_options);
+               packet_buf_write(&req_buf, "filter %s", spec);
        }
        packet_buf_flush(&req_buf);
        state_len = req_buf.len;
@@ -892,12 +888,13 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                                 struct shallow_info *si,
                                 char **pack_lockfile)
 {
+       struct repository *r = the_repository;
        struct ref *ref = copy_ref_list(orig_ref);
        struct object_id oid;
        const char *agent_feature;
        int agent_len;
        struct fetch_negotiator negotiator;
-       fetch_negotiator_init(&negotiator, negotiation_algorithm);
+       fetch_negotiator_init(r, &negotiator);
 
        sort_ref_list(&ref, ref_compare_name);
        QSORT(sought, nr_sought, cmp_ref_by_name);
@@ -911,7 +908,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
 
        if (server_supports("shallow"))
                print_verbose(args, _("Server supports %s"), "shallow");
-       else if (args->depth > 0 || is_repository_shallow(the_repository))
+       else if (args->depth > 0 || is_repository_shallow(r))
                die(_("Server does not support shallow clients"));
        if (args->depth > 0 || args->deepen_since || args->deepen_not)
                args->deepen = 1;
@@ -1112,7 +1109,7 @@ static int add_haves(struct fetch_negotiator *negotiator,
 }
 
 static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
-                             const struct fetch_pack_args *args,
+                             struct fetch_pack_args *args,
                              const struct ref *wants, struct oidset *common,
                              int *haves_to_send, int *in_vain,
                              int sideband_all)
@@ -1153,13 +1150,10 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
        /* Add filter */
        if (server_supports_feature("fetch", "filter", 0) &&
            args->filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
+               const char *spec =
+                       expand_list_objects_filter_spec(&args->filter_options);
                print_verbose(args, _("Server supports filter"));
-               expand_list_objects_filter_spec(&args->filter_options,
-                                               &expanded_filter_spec);
-               packet_buf_write(&req_buf, "filter %s",
-                                expanded_filter_spec.buf);
-               strbuf_release(&expanded_filter_spec);
+               packet_buf_write(&req_buf, "filter %s", spec);
        } else if (args->filter_options.choice) {
                warning("filtering not recognized by server, ignoring");
        }
@@ -1379,6 +1373,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
                                    struct shallow_info *si,
                                    char **pack_lockfile)
 {
+       struct repository *r = the_repository;
        struct ref *ref = copy_ref_list(orig_ref);
        enum fetch_state state = FETCH_CHECK_LOCAL;
        struct oidset common = OIDSET_INIT;
@@ -1386,7 +1381,7 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
        int in_vain = 0;
        int haves_to_send = INITIAL_FLUSH;
        struct fetch_negotiator negotiator;
-       fetch_negotiator_init(&negotiator, negotiation_algorithm);
+       fetch_negotiator_init(r, &negotiator);
        packet_reader_init(&reader, fd[0], NULL, 0,
                           PACKET_READ_CHOMP_NEWLINE |
                           PACKET_READ_DIE_ON_ERR_PACKET);
@@ -1505,8 +1500,6 @@ static void fetch_pack_config(void)
        git_config_get_bool("repack.usedeltabaseoffset", &prefer_ofs_delta);
        git_config_get_bool("fetch.fsckobjects", &fetch_fsck_objects);
        git_config_get_bool("transfer.fsckobjects", &transfer_fsck_objects);
-       git_config_get_string("fetch.negotiationalgorithm",
-                             &negotiation_algorithm);
 
        git_config(fetch_pack_config_cb, NULL);
 }
index 83be89de0aac7c96799635e3b2858a465f440acf..f0d13e4e28470a544516a52146aae80c079467de 100644 (file)
@@ -818,9 +818,6 @@ const char *inet_ntop(int af, const void *src, char *dst, size_t size);
 int git_atexit(void (*handler)(void));
 #endif
 
-typedef void (*try_to_free_t)(size_t);
-try_to_free_t set_try_to_free_routine(try_to_free_t);
-
 static inline size_t st_add(size_t a, size_t b)
 {
        if (unsigned_add_overflows(a, b))
index 6de74ce639cec90fcf7ba1797825526df675547a..fd476b69993c68ddd58e274ae97e0f91c0086951 100755 (executable)
@@ -1340,6 +1340,7 @@ set HEAD {}
 set PARENT {}
 set MERGE_HEAD [list]
 set commit_type {}
+set commit_type_is_amend 0
 set empty_tree {}
 set current_branch {}
 set is_detached 0
@@ -1347,8 +1348,9 @@ set current_diff_path {}
 set is_3way_diff 0
 set is_submodule_diff 0
 set is_conflict_diff 0
-set selected_commit_type new
 set diff_empty_count 0
+set last_revert {}
+set last_revert_enc {}
 
 set nullid "0000000000000000000000000000000000000000"
 set nullid2 "0000000000000000000000000000000000000001"
@@ -1434,7 +1436,7 @@ proc PARENT {} {
 }
 
 proc force_amend {} {
-       global selected_commit_type
+       global commit_type_is_amend
        global HEAD PARENT MERGE_HEAD commit_type
 
        repository_state newType newHEAD newMERGE_HEAD
@@ -1443,7 +1445,7 @@ proc force_amend {} {
        set MERGE_HEAD $newMERGE_HEAD
        set commit_type $newType
 
-       set selected_commit_type amend
+       set commit_type_is_amend 1
        do_select_commit_type
 }
 
@@ -2494,7 +2496,7 @@ proc force_first_diff {after} {
 
 proc toggle_or_diff {mode w args} {
        global file_states file_lists current_diff_path ui_index ui_workdir
-       global last_clicked selected_paths
+       global last_clicked selected_paths file_lists_last_clicked
 
        if {$mode eq "click"} {
                foreach {x y} $args break
@@ -2551,6 +2553,8 @@ proc toggle_or_diff {mode w args} {
        $ui_index tag remove in_sel 0.0 end
        $ui_workdir tag remove in_sel 0.0 end
 
+       set file_lists_last_clicked($w) $path
+
        # Determine the state of the file
        if {[info exists file_states($path)]} {
                set state [lindex $file_states($path) 0]
@@ -2664,6 +2668,32 @@ proc show_less_context {} {
        }
 }
 
+proc focus_widget {widget} {
+       global file_lists last_clicked selected_paths
+       global file_lists_last_clicked
+
+       if {[llength $file_lists($widget)] > 0} {
+               set path $file_lists_last_clicked($widget)
+               set index [lsearch -sorted -exact $file_lists($widget) $path]
+               if {$index < 0} {
+                       set index 0
+                       set path [lindex $file_lists($widget) $index]
+               }
+
+               focus $widget
+               set last_clicked [list $widget [expr $index + 1]]
+               array unset selected_paths
+               set selected_paths($path) 1
+               show_diff $path $widget
+       }
+}
+
+proc toggle_commit_type {} {
+       global commit_type_is_amend
+       set commit_type_is_amend [expr !$commit_type_is_amend]
+       do_select_commit_type
+}
+
 ######################################################################
 ##
 ## ui construction
@@ -2852,19 +2882,11 @@ if {[is_enabled multicommit] || [is_enabled singlecommit]} {
        menu .mbar.commit
 
        if {![is_enabled nocommit]} {
-               .mbar.commit add radiobutton \
-                       -label [mc "New Commit"] \
-                       -command do_select_commit_type \
-                       -variable selected_commit_type \
-                       -value new
-               lappend disable_on_lock \
-                       [list .mbar.commit entryconf [.mbar.commit index last] -state]
-
-               .mbar.commit add radiobutton \
+               .mbar.commit add checkbutton \
                        -label [mc "Amend Last Commit"] \
-                       -command do_select_commit_type \
-                       -variable selected_commit_type \
-                       -value amend
+                       -accelerator $M1T-E \
+                       -variable commit_type_is_amend \
+                       -command do_select_commit_type
                lappend disable_on_lock \
                        [list .mbar.commit entryconf [.mbar.commit index last] -state]
 
@@ -3030,8 +3052,23 @@ unset doc_path doc_url
 wm protocol . WM_DELETE_WINDOW do_quit
 bind all <$M1B-Key-q> do_quit
 bind all <$M1B-Key-Q> do_quit
-bind all <$M1B-Key-w> {destroy [winfo toplevel %W]}
-bind all <$M1B-Key-W> {destroy [winfo toplevel %W]}
+
+set m1b_w_script {
+       set toplvl_win [winfo toplevel %W]
+
+       # If we are destroying the main window, we should call do_quit to take
+       # care of cleanup before exiting the program.
+       if {$toplvl_win eq "."} {
+               do_quit
+       } else {
+               destroy $toplvl_win
+       }
+}
+
+bind all <$M1B-Key-w> $m1b_w_script
+bind all <$M1B-Key-W> $m1b_w_script
+
+unset m1b_w_script
 
 set subcommand_args {}
 proc usage {} {
@@ -3337,18 +3374,10 @@ set ui_comm .vpane.lower.commarea.buffer.frame.t
 set ui_coml .vpane.lower.commarea.buffer.header.l
 
 if {![is_enabled nocommit]} {
-       ${NS}::radiobutton .vpane.lower.commarea.buffer.header.new \
-               -text [mc "New Commit"] \
-               -command do_select_commit_type \
-               -variable selected_commit_type \
-               -value new
-       lappend disable_on_lock \
-               [list .vpane.lower.commarea.buffer.header.new conf -state]
-       ${NS}::radiobutton .vpane.lower.commarea.buffer.header.amend \
+       ${NS}::checkbutton .vpane.lower.commarea.buffer.header.amend \
                -text [mc "Amend Last Commit"] \
-               -command do_select_commit_type \
-               -variable selected_commit_type \
-               -value amend
+               -variable commit_type_is_amend \
+               -command do_select_commit_type
        lappend disable_on_lock \
                [list .vpane.lower.commarea.buffer.header.amend conf -state]
 }
@@ -3373,7 +3402,6 @@ pack $ui_coml -side left -fill x
 
 if {![is_enabled nocommit]} {
        pack .vpane.lower.commarea.buffer.header.amend -side right
-       pack .vpane.lower.commarea.buffer.header.new -side right
 }
 
 textframe .vpane.lower.commarea.buffer.frame
@@ -3387,10 +3415,16 @@ ttext $ui_comm -background white -foreground black \
        -relief sunken \
        -width $repo_config(gui.commitmsgwidth) -height 9 -wrap none \
        -font font_diff \
+       -xscrollcommand {.vpane.lower.commarea.buffer.frame.sbx set} \
        -yscrollcommand {.vpane.lower.commarea.buffer.frame.sby set}
+${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sbx \
+       -orient horizontal \
+       -command [list $ui_comm xview]
 ${NS}::scrollbar .vpane.lower.commarea.buffer.frame.sby \
+       -orient vertical \
        -command [list $ui_comm yview]
 
+pack .vpane.lower.commarea.buffer.frame.sbx -side bottom -fill x
 pack .vpane.lower.commarea.buffer.frame.sby -side right -fill y
 pack $ui_comm -side left -fill y
 pack .vpane.lower.commarea.buffer.header -side top -fill x
@@ -3606,15 +3640,31 @@ set ctxm .vpane.lower.diff.body.ctxm
 menu $ctxm -tearoff 0
 $ctxm add command \
        -label [mc "Apply/Reverse Hunk"] \
-       -command {apply_hunk $cursorX $cursorY}
+       -command {apply_or_revert_hunk $cursorX $cursorY 0}
 set ui_diff_applyhunk [$ctxm index last]
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyhunk -state]
 $ctxm add command \
        -label [mc "Apply/Reverse Line"] \
-       -command {apply_range_or_line $cursorX $cursorY; do_rescan}
+       -command {apply_or_revert_range_or_line $cursorX $cursorY 0; do_rescan}
 set ui_diff_applyline [$ctxm index last]
 lappend diff_actions [list $ctxm entryconf $ui_diff_applyline -state]
 $ctxm add separator
+$ctxm add command \
+       -label [mc "Revert Hunk"] \
+       -command {apply_or_revert_hunk $cursorX $cursorY 1}
+set ui_diff_reverthunk [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_reverthunk -state]
+$ctxm add command \
+       -label [mc "Revert Line"] \
+       -command {apply_or_revert_range_or_line $cursorX $cursorY 1; do_rescan}
+set ui_diff_revertline [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_revertline -state]
+$ctxm add command \
+       -label [mc "Undo Last Revert"] \
+       -command {undo_last_revert; do_rescan}
+set ui_diff_undorevert [$ctxm index last]
+lappend diff_actions [list $ctxm entryconf $ui_diff_undorevert -state]
+$ctxm add separator
 $ctxm add command \
        -label [mc "Show Less Context"] \
        -command show_less_context
@@ -3693,7 +3743,7 @@ proc has_textconv {path} {
 }
 
 proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
-       global current_diff_path file_states
+       global current_diff_path file_states last_revert
        set ::cursorX $x
        set ::cursorY $y
        if {[info exists file_states($current_diff_path)]} {
@@ -3707,19 +3757,28 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
                tk_popup $ctxmsm $X $Y
        } else {
                set has_range [expr {[$::ui_diff tag nextrange sel 0.0] != {}}]
+               set u [mc "Undo Last Revert"]
                if {$::ui_index eq $::current_diff_side} {
                        set l [mc "Unstage Hunk From Commit"]
+                       set h [mc "Revert Hunk"]
+
                        if {$has_range} {
                                set t [mc "Unstage Lines From Commit"]
+                               set r [mc "Revert Lines"]
                        } else {
                                set t [mc "Unstage Line From Commit"]
+                               set r [mc "Revert Line"]
                        }
                } else {
                        set l [mc "Stage Hunk For Commit"]
+                       set h [mc "Revert Hunk"]
+
                        if {$has_range} {
                                set t [mc "Stage Lines For Commit"]
+                               set r [mc "Revert Lines"]
                        } else {
                                set t [mc "Stage Line For Commit"]
+                               set r [mc "Revert Line"]
                        }
                }
                if {$::is_3way_diff
@@ -3730,11 +3789,35 @@ proc popup_diff_menu {ctxm ctxmmg ctxmsm x y X Y} {
                        || [string match {T?} $state]
                        || [has_textconv $current_diff_path]} {
                        set s disabled
+                       set revert_state disabled
                } else {
                        set s normal
+
+                       # Only allow reverting changes in the working tree. If
+                       # the user wants to revert changes in the index, they
+                       # need to unstage those first.
+                       if {$::ui_workdir eq $::current_diff_side} {
+                               set revert_state normal
+                       } else {
+                               set revert_state disabled
+                       }
+               }
+
+               if {$last_revert eq {}} {
+                       set undo_state disabled
+               } else {
+                       set undo_state normal
                }
+
                $ctxm entryconf $::ui_diff_applyhunk -state $s -label $l
                $ctxm entryconf $::ui_diff_applyline -state $s -label $t
+               $ctxm entryconf $::ui_diff_revertline -state $revert_state \
+                       -label $r
+               $ctxm entryconf $::ui_diff_reverthunk -state $revert_state \
+                       -label $h
+               $ctxm entryconf $::ui_diff_undorevert -state $undo_state \
+                       -label $u
+
                tk_popup $ctxm $X $Y
        }
 }
@@ -3861,6 +3944,8 @@ bind .   <$M1B-Key-j> do_revert_selection
 bind .   <$M1B-Key-J> do_revert_selection
 bind .   <$M1B-Key-i> do_add_all
 bind .   <$M1B-Key-I> do_add_all
+bind .   <$M1B-Key-e> toggle_commit_type
+bind .   <$M1B-Key-E> toggle_commit_type
 bind .   <$M1B-Key-minus> {show_less_context;break}
 bind .   <$M1B-Key-KP_Subtract> {show_less_context;break}
 bind .   <$M1B-Key-equal> {show_more_context;break}
@@ -3877,6 +3962,14 @@ foreach i [list $ui_index $ui_workdir] {
 }
 unset i
 
+bind .   <Alt-Key-1> {focus_widget $::ui_workdir}
+bind .   <Alt-Key-2> {focus_widget $::ui_index}
+bind .   <Alt-Key-3> {focus $::ui_diff}
+bind .   <Alt-Key-4> {focus $::ui_comm}
+
+set file_lists_last_clicked($ui_index) {}
+set file_lists_last_clicked($ui_workdir) {}
+
 set file_lists($ui_index) [list]
 set file_lists($ui_workdir) [list]
 
index 9e7412c446f715588b9cfe21654a3447e8680e12..a5228297db2c6bcccaf61d906143ecfffacf9822 100644 (file)
@@ -389,7 +389,7 @@ $err
 }
 
 method _after_readtree {} {
-       global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
+       global commit_type HEAD MERGE_HEAD PARENT
        global current_branch is_detached
        global ui_comm
 
@@ -490,12 +490,12 @@ method _update_repo_state {} {
        #    amend mode our file lists are accurate and we can avoid
        #    the rescan.
        #
-       global selected_commit_type commit_type HEAD MERGE_HEAD PARENT
+       global commit_type_is_amend commit_type HEAD MERGE_HEAD PARENT
        global ui_comm
 
        unlock_index
        set name [_name $this]
-       set selected_commit_type new
+       set commit_type_is_amend 0
        if {[string match amend* $commit_type]} {
                $ui_comm delete 0.0 end
                $ui_comm edit reset
index 75ea965dacd71289dd6b965a51f40bb3c1298f5b..b516aa29906911a6984f2e9ea539c2e25d23ae66 100644 (file)
@@ -333,7 +333,7 @@ proc commit_writetree {curHEAD msg_p} {
 proc commit_committree {fd_wt curHEAD msg_p} {
        global HEAD PARENT MERGE_HEAD commit_type commit_author
        global current_branch
-       global ui_comm selected_commit_type
+       global ui_comm commit_type_is_amend
        global file_states selected_paths rescan_active
        global repo_config
        global env
@@ -467,8 +467,8 @@ A rescan will be automatically started now.
 
        # -- Update in memory status
        #
-       set selected_commit_type new
        set commit_type normal
+       set commit_type_is_amend 0
        set HEAD $cmt_id
        set PARENT $cmt_id
        set MERGE_HEAD [list]
index 68c4a6c7366f864bcfc500483c83b15860a2d185..958a0fa2191b1dbcf3b5f92de7a1b77ac03fb152 100644 (file)
@@ -55,7 +55,7 @@ proc reshow_diff {{after {}}} {
 
 proc force_diff_encoding {enc} {
        global current_diff_path
-       
+
        if {$current_diff_path ne {}} {
                force_path_encoding $current_diff_path $enc
                reshow_diff
@@ -567,24 +567,31 @@ proc read_diff {fd conflict_size cont_info} {
        }
 }
 
-proc apply_hunk {x y} {
+proc apply_or_revert_hunk {x y revert} {
        global current_diff_path current_diff_header current_diff_side
-       global ui_diff ui_index file_states
+       global ui_diff ui_index file_states last_revert last_revert_enc
 
        if {$current_diff_path eq {} || $current_diff_header eq {}} return
        if {![lock_index apply_hunk]} return
 
-       set apply_cmd {apply --cached --whitespace=nowarn}
+       set apply_cmd {apply --whitespace=nowarn}
        set mi [lindex $file_states($current_diff_path) 0]
        if {$current_diff_side eq $ui_index} {
                set failed_msg [mc "Failed to unstage selected hunk."]
-               lappend apply_cmd --reverse
+               lappend apply_cmd --reverse --cached
                if {[string index $mi 0] ne {M}} {
                        unlock_index
                        return
                }
        } else {
-               set failed_msg [mc "Failed to stage selected hunk."]
+               if {$revert} {
+                       set failed_msg [mc "Failed to revert selected hunk."]
+                       lappend apply_cmd --reverse
+               } else {
+                       set failed_msg [mc "Failed to stage selected hunk."]
+                       lappend apply_cmd --cached
+               }
+
                if {[string index $mi 1] ne {M}} {
                        unlock_index
                        return
@@ -603,29 +610,40 @@ proc apply_hunk {x y} {
                set e_lno end
        }
 
+       set wholepatch "$current_diff_header[$ui_diff get $s_lno $e_lno]"
+
        if {[catch {
                set enc [get_path_encoding $current_diff_path]
                set p [eval git_write $apply_cmd]
                fconfigure $p -translation binary -encoding $enc
-               puts -nonewline $p $current_diff_header
-               puts -nonewline $p [$ui_diff get $s_lno $e_lno]
+               puts -nonewline $p $wholepatch
                close $p} err]} {
                error_popup "$failed_msg\n\n$err"
                unlock_index
                return
        }
 
+       if {$revert} {
+               # Save a copy of this patch for undoing reverts.
+               set last_revert $wholepatch
+               set last_revert_enc $enc
+       }
+
        $ui_diff conf -state normal
        $ui_diff delete $s_lno $e_lno
        $ui_diff conf -state disabled
 
+       # Check if the hunk was the last one in the file.
        if {[$ui_diff get 1.0 end] eq "\n"} {
                set o _
        } else {
                set o ?
        }
 
-       if {$current_diff_side eq $ui_index} {
+       # Update the status flags.
+       if {$revert} {
+               set mi [string index $mi 0]$o
+       } elseif {$current_diff_side eq $ui_index} {
                set mi ${o}M
        } elseif {[string index $mi 0] eq {_}} {
                set mi M$o
@@ -640,9 +658,9 @@ proc apply_hunk {x y} {
        }
 }
 
-proc apply_range_or_line {x y} {
+proc apply_or_revert_range_or_line {x y revert} {
        global current_diff_path current_diff_header current_diff_side
-       global ui_diff ui_index file_states
+       global ui_diff ui_index file_states last_revert
 
        set selected [$ui_diff tag nextrange sel 0.0]
 
@@ -660,19 +678,27 @@ proc apply_range_or_line {x y} {
        if {$current_diff_path eq {} || $current_diff_header eq {}} return
        if {![lock_index apply_hunk]} return
 
-       set apply_cmd {apply --cached --whitespace=nowarn}
+       set apply_cmd {apply --whitespace=nowarn}
        set mi [lindex $file_states($current_diff_path) 0]
        if {$current_diff_side eq $ui_index} {
                set failed_msg [mc "Failed to unstage selected line."]
                set to_context {+}
-               lappend apply_cmd --reverse
+               lappend apply_cmd --reverse --cached
                if {[string index $mi 0] ne {M}} {
                        unlock_index
                        return
                }
        } else {
-               set failed_msg [mc "Failed to stage selected line."]
-               set to_context {-}
+               if {$revert} {
+                       set failed_msg [mc "Failed to revert selected line."]
+                       set to_context {+}
+                       lappend apply_cmd --reverse
+               } else {
+                       set failed_msg [mc "Failed to stage selected line."]
+                       set to_context {-}
+                       lappend apply_cmd --cached
+               }
+
                if {[string index $mi 1] ne {M}} {
                        unlock_index
                        return
@@ -830,7 +856,47 @@ proc apply_range_or_line {x y} {
                puts -nonewline $p $wholepatch
                close $p} err]} {
                error_popup "$failed_msg\n\n$err"
+               unlock_index
+               return
+       }
+
+       if {$revert} {
+               # Save a copy of this patch for undoing reverts.
+               set last_revert $current_diff_header$wholepatch
+               set last_revert_enc $enc
        }
 
        unlock_index
 }
+
+# Undo the last line/hunk reverted. When hunks and lines are reverted, a copy
+# of the diff applied is saved. Re-apply that diff to undo the revert.
+#
+# Right now, we only use a single variable to hold the copy, and not a
+# stack/deque for simplicity, so multiple undos are not possible. Maybe this
+# can be added if the need for something like this is felt in the future.
+proc undo_last_revert {} {
+       global last_revert current_diff_path current_diff_header
+       global last_revert_enc
+
+       if {$last_revert eq {}} return
+       if {![lock_index apply_hunk]} return
+
+       set apply_cmd {apply --whitespace=nowarn}
+       set failed_msg [mc "Failed to undo last revert."]
+
+       if {[catch {
+               set enc $last_revert_enc
+               set p [eval git_write $apply_cmd]
+               fconfigure $p -translation binary -encoding $enc
+               puts -nonewline $p $last_revert
+               close $p} err]} {
+               error_popup "$failed_msg\n\n$err"
+               unlock_index
+               return
+       }
+
+       set last_revert {}
+
+       unlock_index
+}
index b588db11d9fc46b6c4c33274c3c5f969dfe43e5e..e07b7a376230f50a66f2450255121798409667f8 100644 (file)
@@ -466,19 +466,19 @@ proc do_revert_selection {} {
 }
 
 proc do_select_commit_type {} {
-       global commit_type selected_commit_type
+       global commit_type commit_type_is_amend
 
-       if {$selected_commit_type eq {new}
+       if {$commit_type_is_amend == 0
                && [string match amend* $commit_type]} {
                create_new_commit
-       } elseif {$selected_commit_type eq {amend}
+       } elseif {$commit_type_is_amend == 1
                && ![string match amend* $commit_type]} {
                load_last_commit
 
                # The amend request was rejected...
                #
                if {![string match amend* $commit_type]} {
-                       set selected_commit_type new
+                       set commit_type_is_amend 0
                }
        }
 }
index a14d7a16b2dd1162fa8572d776f4adee8a0ce91f..abe4805adedb3c3559bf0048ae49df2e2ac37337 100755 (executable)
@@ -3404,6 +3404,8 @@ set rectmask {
 }
 image create bitmap reficon-H -background black -foreground "#00ff00" \
     -data $rectdata -maskdata $rectmask
+image create bitmap reficon-R -background black -foreground "#ffddaa" \
+    -data $rectdata -maskdata $rectmask
 image create bitmap reficon-o -background black -foreground "#ddddff" \
     -data $rectdata -maskdata $rectmask
 
@@ -7016,6 +7018,7 @@ proc commit_descriptor {p} {
 
 # append some text to the ctext widget, and make any SHA1 ID
 # that we know about be a clickable link.
+# Also look for URLs of the form "http[s]://..." and make them web links.
 proc appendwithlinks {text tags} {
     global ctext linknum curview
 
@@ -7032,6 +7035,18 @@ proc appendwithlinks {text tags} {
        setlink $linkid link$linknum
        incr linknum
     }
+    set wlinks [regexp -indices -all -inline -line \
+                   {https?://[^[:space:]]+} $text]
+    foreach l $wlinks {
+       set s2 [lindex $l 0]
+       set e2 [lindex $l 1]
+       set url [string range $text $s2 $e2]
+       incr e2
+       $ctext tag delete link$linknum
+       $ctext tag add link$linknum "$start + $s2 c" "$start + $e2 c"
+       setwlink $url link$linknum
+       incr linknum
+    }
 }
 
 proc setlink {id lk} {
@@ -7064,6 +7079,18 @@ proc setlink {id lk} {
     }
 }
 
+proc setwlink {url lk} {
+    global ctext
+    global linkfgcolor
+    global web_browser
+
+    if {$web_browser eq {}} return
+    $ctext tag conf $lk -foreground $linkfgcolor -underline 1
+    $ctext tag bind $lk <1> [list browseweb $url]
+    $ctext tag bind $lk <Enter> {linkcursor %W 1}
+    $ctext tag bind $lk <Leave> {linkcursor %W -1}
+}
+
 proc appendshortlink {id {pre {}} {post {}}} {
     global ctext linknum
 
@@ -7098,6 +7125,16 @@ proc linkcursor {w inc} {
     }
 }
 
+proc browseweb {url} {
+    global web_browser
+
+    if {$web_browser eq {}} return
+    # Use eval here in case $web_browser is a command plus some arguments
+    if {[catch {eval exec $web_browser [list $url] &} err]} {
+       error_popup "[mc "Error starting web browser:"] $err"
+    }
+}
+
 proc viewnextline {dir} {
     global canv linespc
 
@@ -8191,11 +8228,11 @@ proc parseblobdiffline {ids line} {
        } else {
            $ctext insert end "$line\n" filesep
        }
-    } elseif {![string compare -length 3 "  >" $line]} {
+    } elseif {$currdiffsubmod != "" && ![string compare -length 3 "  >" $line]} {
        set $currdiffsubmod ""
        set line [encoding convertfrom $diffencoding $line]
        $ctext insert end "$line\n" dresult
-    } elseif {![string compare -length 3 "  <" $line]} {
+    } elseif {$currdiffsubmod != "" && ![string compare -length 3 "  <" $line]} {
        set $currdiffsubmod ""
        set line [encoding convertfrom $diffencoding $line]
        $ctext insert end "$line\n" d0
@@ -10022,6 +10059,7 @@ proc sel_reflist {w x y} {
     set n [lindex $ref 0]
     switch -- [lindex $ref 1] {
        "H" {selbyid $headids($n)}
+       "R" {selbyid $headids($n)}
        "T" {selbyid $tagids($n)}
        "o" {selbyid $otherrefids($n)}
     }
@@ -10051,7 +10089,11 @@ proc refill_reflist {} {
     foreach n [array names headids] {
        if {[string match $reflistfilter $n]} {
            if {[commitinview $headids($n) $curview]} {
-               lappend refs [list $n H]
+               if {[string match "remotes/*" $n]} {
+                   lappend refs [list $n R]
+               } else {
+                   lappend refs [list $n H]
+               }
            } else {
                interestedin $headids($n) {run refill_reflist}
            }
@@ -11488,7 +11530,7 @@ proc create_prefs_page {w} {
 proc prefspage_general {notebook} {
     global NS maxwidth maxgraphpct showneartags showlocalchanges
     global tabstop limitdiffs autoselect autosellen extdifftool perfile_attrs
-    global hideremotes want_ttk have_ttk maxrefs
+    global hideremotes want_ttk have_ttk maxrefs web_browser
 
     set page [create_prefs_page $notebook.general]
 
@@ -11539,6 +11581,13 @@ proc prefspage_general {notebook} {
     pack configure $page.extdifff.l -padx 10
     grid x $page.extdifff $page.extdifft -sticky ew
 
+    ${NS}::entry $page.webbrowser -textvariable web_browser
+    ${NS}::frame $page.webbrowserf
+    ${NS}::label $page.webbrowserf.l -text [mc "Web browser" ]
+    pack $page.webbrowserf.l -side left
+    pack configure $page.webbrowserf.l -padx 10
+    grid x $page.webbrowserf $page.webbrowser -sticky ew
+
     ${NS}::label $page.lgen -text [mc "General options"]
     grid $page.lgen - -sticky w -pady 10
     ${NS}::checkbutton $page.want_ttk -variable want_ttk \
@@ -12310,6 +12359,7 @@ if {[tk windowingsystem] eq "win32"} {
     set bgcolor SystemWindow
     set fgcolor SystemWindowText
     set selectbgcolor SystemHighlight
+    set web_browser "cmd /c start"
 } else {
     set uicolor grey85
     set uifgcolor black
@@ -12317,6 +12367,11 @@ if {[tk windowingsystem] eq "win32"} {
     set bgcolor white
     set fgcolor black
     set selectbgcolor gray85
+    if {[tk windowingsystem] eq "aqua"} {
+       set web_browser "open"
+    } else {
+       set web_browser "xdg-open"
+    }
 }
 set diffcolors {red "#00a000" blue}
 set diffcontext 3
@@ -12390,6 +12445,7 @@ set config_variables {
     filesepbgcolor filesepfgcolor linehoverbgcolor linehoverfgcolor
     linehoveroutlinecolor mainheadcirclecolor workingfilescirclecolor
     indexcirclecolor circlecolors linkfgcolor circleoutlinecolor
+    web_browser
 }
 foreach var $config_variables {
     config_init_trace $var
diff --git a/gitk-git/po/zh_cn.po b/gitk-git/po/zh_cn.po
new file mode 100644 (file)
index 0000000..17b7f89
--- /dev/null
@@ -0,0 +1,1367 @@
+# Translation of gitk to Simplified Chinese.
+#
+# Translators:
+# YanKe <imyanke@163.com>, 2017
+
+msgid ""
+msgstr ""
+"Project-Id-Version: Git Chinese Localization Project\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-02-28 23:11+0800\n"
+"PO-Revision-Date: 2017-03-11 02:27+0800\n"
+"Last-Translator: YanKe <imyanke@163.com>\n"
+"Language-Team: Chinese\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Language: zh_CN\n"
+
+#: gitk:140
+msgid "Couldn't get list of unmerged files:"
+msgstr "不能获取未合并文件列表:"
+
+#: gitk:212 gitk:2403
+msgid "Color words"
+msgstr "着色显示差异"
+
+#: gitk:217 gitk:2403 gitk:8249 gitk:8282
+msgid "Markup words"
+msgstr "标记显示差异"
+
+#: gitk:324
+msgid "Error parsing revisions:"
+msgstr "解析版本错误:"
+
+#: gitk:380
+msgid "Error executing --argscmd command:"
+msgstr "运行 --argscmd命令出错"
+
+#: gitk:393
+msgid "No files selected: --merge specified but no files are unmerged."
+msgstr "没有选中文件:--指定merge参数但没有未合并的文件。"
+
+#: gitk:396
+msgid ""
+"No files selected: --merge specified but no unmerged files are within file "
+"limit."
+msgstr "没有选中文件:--指定merge参数但没有未合并的文件在文件中"
+
+#: gitk:418 gitk:566
+msgid "Error executing git log:"
+msgstr "执行git log命令出错:"
+
+#: gitk:436 gitk:582
+msgid "Reading"
+msgstr "读取中"
+
+#: gitk:496 gitk:4549
+msgid "Reading commits..."
+msgstr "提交记录读取中..."
+
+#: gitk:499 gitk:1641 gitk:4552
+msgid "No commits selected"
+msgstr "未选中任何提交"
+
+#: gitk:1449 gitk:4069 gitk:12583
+msgid "Command line"
+msgstr "命令行"
+
+#: gitk:1515
+msgid "Can't parse git log output:"
+msgstr "不能解析git log输出:"
+
+#: gitk:1744
+msgid "No commit information available"
+msgstr "无可用提交信息"
+
+#: gitk:1907 gitk:1936 gitk:4339 gitk:9789 gitk:11388 gitk:11668
+msgid "OK"
+msgstr "确定"
+
+#: gitk:1938 gitk:4341 gitk:9225 gitk:9304 gitk:9434 gitk:9520 gitk:9791
+#: gitk:11389 gitk:11669
+msgid "Cancel"
+msgstr "取消"
+
+#: gitk:2087
+msgid "&Update"
+msgstr "更新"
+
+#: gitk:2088
+msgid "&Reload"
+msgstr "重新加载"
+
+#: gitk:2089
+msgid "Reread re&ferences"
+msgstr "重新读取引用"
+
+#: gitk:2090
+msgid "&List references"
+msgstr "列出引用(分支以及tag)"
+
+#: gitk:2092
+msgid "Start git &gui"
+msgstr "启动git gui客户端"
+
+#: gitk:2094
+msgid "&Quit"
+msgstr "退出"
+
+#: gitk:2086
+msgid "&File"
+msgstr "文件"
+
+#: gitk:2098
+msgid "&Preferences"
+msgstr "偏好设置"
+
+#: gitk:2097
+msgid "&Edit"
+msgstr "编辑"
+
+#: gitk:2102
+msgid "&New view..."
+msgstr "新视图..."
+
+#: gitk:2103
+msgid "&Edit view..."
+msgstr "编辑视图..."
+
+#: gitk:2104
+msgid "&Delete view"
+msgstr "删除视图"
+
+#: gitk:2106
+msgid "&All files"
+msgstr "所有文件"
+
+#: gitk:2101
+msgid "&View"
+msgstr "视图"
+
+#: gitk:2111 gitk:2121
+msgid "&About gitk"
+msgstr "关于gitk"
+
+#: gitk:2112 gitk:2126
+msgid "&Key bindings"
+msgstr "快捷键"
+
+#: gitk:2110 gitk:2125
+msgid "&Help"
+msgstr "帮助"
+
+#: gitk:2203 gitk:8681
+msgid "SHA1 ID:"
+msgstr "SHA1 ID:"
+
+#: gitk:2247
+msgid "Row"
+msgstr "行"
+
+#: gitk:2285
+msgid "Find"
+msgstr "查找"
+
+#: gitk:2313
+msgid "commit"
+msgstr "提交"
+
+#: gitk:2317 gitk:2319 gitk:4711 gitk:4734 gitk:4758 gitk:6779 gitk:6851
+#: gitk:6936
+msgid "containing:"
+msgstr "包含:"
+
+#: gitk:2320 gitk:3550 gitk:3555 gitk:4787
+msgid "touching paths:"
+msgstr "影响路径:"
+
+#: gitk:2321 gitk:4801
+msgid "adding/removing string:"
+msgstr "增加/删除字符串:"
+
+#: gitk:2322 gitk:4803
+msgid "changing lines matching:"
+msgstr "改变行匹配:"
+
+#: gitk:2331 gitk:2333 gitk:4790
+msgid "Exact"
+msgstr "精确匹配"
+
+#: gitk:2333 gitk:4878 gitk:6747
+msgid "IgnCase"
+msgstr "忽略大小写"
+
+#: gitk:2333 gitk:4760 gitk:4876 gitk:6743
+msgid "Regexp"
+msgstr "正则"
+
+#: gitk:2335 gitk:2336 gitk:4898 gitk:4928 gitk:4935 gitk:6872 gitk:6940
+msgid "All fields"
+msgstr "所有字段"
+
+#: gitk:2336 gitk:4895 gitk:4928 gitk:6810
+msgid "Headline"
+msgstr "标题"
+
+#: gitk:2337 gitk:4895 gitk:6810 gitk:6940 gitk:7413
+msgid "Comments"
+msgstr "提交注释"
+
+#: gitk:2337 gitk:4895 gitk:4900 gitk:4935 gitk:6810 gitk:7348 gitk:8859
+#: gitk:8874
+msgid "Author"
+msgstr "作者"
+
+#: gitk:2337 gitk:4895 gitk:6810 gitk:7350
+msgid "Committer"
+msgstr "提交者"
+
+#: gitk:2371
+msgid "Search"
+msgstr "搜索"
+
+#: gitk:2379
+msgid "Diff"
+msgstr "差异"
+
+#: gitk:2381
+msgid "Old version"
+msgstr "老版本"
+
+#: gitk:2383
+msgid "New version"
+msgstr "新版本"
+
+#: gitk:2386
+msgid "Lines of context"
+msgstr "Diff上下文显示行数"
+
+#: gitk:2396
+msgid "Ignore space change"
+msgstr "忽略空格修改"
+
+#: gitk:2400 gitk:2402 gitk:7983 gitk:8235
+msgid "Line diff"
+msgstr "按行显示差异"
+
+#: gitk:2467
+msgid "Patch"
+msgstr "补丁"
+
+#: gitk:2469
+msgid "Tree"
+msgstr "树"
+
+#: gitk:2639 gitk:2660
+msgid "Diff this -> selected"
+msgstr "比较从当前提交到选中提交的差异"
+
+#: gitk:2640 gitk:2661
+msgid "Diff selected -> this"
+msgstr "比较从选中提交到当前提交的差异"
+
+#: gitk:2641 gitk:2662
+msgid "Make patch"
+msgstr "制作补丁"
+
+#: gitk:2642 gitk:9283
+msgid "Create tag"
+msgstr "创建tag"
+
+#: gitk:2643
+msgid "Copy commit summary"
+msgstr "复制提交摘要"
+
+#: gitk:2644 gitk:9414
+msgid "Write commit to file"
+msgstr "写入提交到文件"
+
+#: gitk:2645
+msgid "Create new branch"
+msgstr "创建新分支"
+
+#: gitk:2646
+msgid "Cherry-pick this commit"
+msgstr "在此提交运用补丁(cherry-pick)命令"
+
+#: gitk:2647
+msgid "Reset HEAD branch to here"
+msgstr "将分支头(HEAD)重置到此处"
+
+#: gitk:2648
+msgid "Mark this commit"
+msgstr "标记此提交"
+
+#: gitk:2649
+msgid "Return to mark"
+msgstr "返回到标记"
+
+#: gitk:2650
+msgid "Find descendant of this and mark"
+msgstr "查找本次提交的子提交并标记"
+
+#: gitk:2651
+msgid "Compare with marked commit"
+msgstr "和已标记的提交作比较"
+
+#: gitk:2652 gitk:2663
+msgid "Diff this -> marked commit"
+msgstr "比较从当前提交到已标记提交的差异"
+
+#: gitk:2653 gitk:2664
+msgid "Diff marked commit -> this"
+msgstr "比较从已标记提交到当前提交的差异"
+
+#: gitk:2654
+msgid "Revert this commit"
+msgstr "撤销(revert)此提交"
+
+#: gitk:2670
+msgid "Check out this branch"
+msgstr "检出(checkout)此分支"
+
+#: gitk:2671
+msgid "Rename this branch"
+msgstr "重命名(Rename)此分支"
+
+#: gitk:2672
+msgid "Remove this branch"
+msgstr "删除(Remove)此分支"
+
+#: gitk:2673
+msgid "Copy branch name"
+msgstr "复制分支名称"
+
+#: gitk:2680
+msgid "Highlight this too"
+msgstr "高亮此处"
+
+#: gitk:2681
+msgid "Highlight this only"
+msgstr "只高亮此处"
+
+#: gitk:2682
+msgid "External diff"
+msgstr "外部diff"
+
+#: gitk:2683
+msgid "Blame parent commit"
+msgstr "Blame父提交"
+
+#: gitk:2684
+msgid "Copy path"
+msgstr "复制路径"
+
+#: gitk:2691
+msgid "Show origin of this line"
+msgstr "显示此行原始提交"
+
+#: gitk:2692
+msgid "Run git gui blame on this line"
+msgstr "在此行运行git gui客户端的blame"
+
+#: gitk:3036
+msgid "About gitk"
+msgstr "关于gitk"
+
+#: gitk:3038
+msgid ""
+"\n"
+"Gitk - a commit viewer for git\n"
+"\n"
+"Copyright © 2005-2016 Paul Mackerras\n"
+"\n"
+"Use and redistribute under the terms of the GNU General Public License"
+msgstr "\nGitk — 一个git的提交查看器\n\n© 2005-2016 Paul Mackerras\n\n在GNU许可证下使用以及分发"
+
+#: gitk:3046 gitk:3113 gitk:10004
+msgid "Close"
+msgstr "关闭"
+
+#: gitk:3067
+msgid "Gitk key bindings"
+msgstr "Gitk快捷键"
+
+#: gitk:3070
+msgid "Gitk key bindings:"
+msgstr "Gitk快捷键:"
+
+#: gitk:3072
+#, tcl-format
+msgid "<%s-Q>\t\tQuit"
+msgstr "<%s-Q>\t\t退出"
+
+#: gitk:3073
+#, tcl-format
+msgid "<%s-W>\t\tClose window"
+msgstr "<%s-W>\t\t关闭窗口"
+
+#: gitk:3074
+msgid "<Home>\t\tMove to first commit"
+msgstr "<Home>\t\t移动到第一次提交"
+
+#: gitk:3075
+msgid "<End>\t\tMove to last commit"
+msgstr "<End>\t\t移动到最后一次提交"
+
+#: gitk:3076
+msgid "<Up>, p, k\tMove up one commit"
+msgstr "<Up>, p, k\t移动到上一次提交"
+
+#: gitk:3077
+msgid "<Down>, n, j\tMove down one commit"
+msgstr "<Down>, n, j\t移动到下一次提交"
+
+#: gitk:3078
+msgid "<Left>, z, h\tGo back in history list"
+msgstr "<Left>, z, h\t历史列表的上一项"
+
+#: gitk:3079
+msgid "<Right>, x, l\tGo forward in history list"
+msgstr "<Right>, x, l\t历史列表的下一项"
+
+#: gitk:3080
+#, tcl-format
+msgid "<%s-n>\tGo to n-th parent of current commit in history list"
+msgstr "<%s-n>\t在历史列表中前往本次提交的第n个父提交"
+
+#: gitk:3081
+msgid "<PageUp>\tMove up one page in commit list"
+msgstr "<PageUp>\t上一页提交列表"
+
+#: gitk:3082
+msgid "<PageDown>\tMove down one page in commit list"
+msgstr "<PageDown>\t下一页提交列表"
+
+#: gitk:3083
+#, tcl-format
+msgid "<%s-Home>\tScroll to top of commit list"
+msgstr "<%s-Home>\t滚动到提交列表顶部"
+
+#: gitk:3084
+#, tcl-format
+msgid "<%s-End>\tScroll to bottom of commit list"
+msgstr "<%s-End>\t滚动到提交列表底部"
+
+#: gitk:3085
+#, tcl-format
+msgid "<%s-Up>\tScroll commit list up one line"
+msgstr "<%s-Up>\t向上滚动一行提交列表"
+
+#: gitk:3086
+#, tcl-format
+msgid "<%s-Down>\tScroll commit list down one line"
+msgstr "<%s-Down>\t向下滚动一行提交列表"
+
+#: gitk:3087
+#, tcl-format
+msgid "<%s-PageUp>\tScroll commit list up one page"
+msgstr "<%s-PageUp>\t向上滚动一页提交列表"
+
+#: gitk:3088
+#, tcl-format
+msgid "<%s-PageDown>\tScroll commit list down one page"
+msgstr "<%s-PageDown>\t向下滚动一页提交列表"
+
+#: gitk:3089
+msgid "<Shift-Up>\tFind backwards (upwards, later commits)"
+msgstr "<Shift-Up>\t向后查找(向上的,更晚的提交)"
+
+#: gitk:3090
+msgid "<Shift-Down>\tFind forwards (downwards, earlier commits)"
+msgstr "<Shift-Down>\t向前查找(向下的,更早的提交)"
+
+#: gitk:3091
+msgid "<Delete>, b\tScroll diff view up one page"
+msgstr "<Delete>, b\t向上滚动diff视图一页"
+
+#: gitk:3092
+msgid "<Backspace>\tScroll diff view up one page"
+msgstr "<Backspace>\t向上滚动diff视图一页"
+
+#: gitk:3093
+msgid "<Space>\t\tScroll diff view down one page"
+msgstr "<Space>\t\t向下滚动diff视图一页"
+
+#: gitk:3094
+msgid "u\t\tScroll diff view up 18 lines"
+msgstr "u\t\t向上滚动diff视图18行"
+
+#: gitk:3095
+msgid "d\t\tScroll diff view down 18 lines"
+msgstr "d\t\t向下滚动diff视图18行"
+
+#: gitk:3096
+#, tcl-format
+msgid "<%s-F>\t\tFind"
+msgstr "<%s-F>\t\t查找"
+
+#: gitk:3097
+#, tcl-format
+msgid "<%s-G>\t\tMove to next find hit"
+msgstr "<%s-G>\t\t移动到下一次查找命中"
+
+#: gitk:3098
+msgid "<Return>\tMove to next find hit"
+msgstr "<Return>\t\t移动到下一次查找命中"
+
+#: gitk:3099
+msgid "g\t\tGo to commit"
+msgstr "g\t\t转到提交"
+
+#: gitk:3100
+msgid "/\t\tFocus the search box"
+msgstr "/\t\t选中搜索框"
+
+#: gitk:3101
+msgid "?\t\tMove to previous find hit"
+msgstr "?\t\t移动到上一次查找命中"
+
+#: gitk:3102
+msgid "f\t\tScroll diff view to next file"
+msgstr "f\t\t滚动diff视图到下一个文件"
+
+#: gitk:3103
+#, tcl-format
+msgid "<%s-S>\t\tSearch for next hit in diff view"
+msgstr "<%s-S>\t\t在diff视图中查找下一此命中"
+
+#: gitk:3104
+#, tcl-format
+msgid "<%s-R>\t\tSearch for previous hit in diff view"
+msgstr "<%s-R>\t\t在diff视图中查找上一次命中"
+
+#: gitk:3105
+#, tcl-format
+msgid "<%s-KP+>\tIncrease font size"
+msgstr "<%s-KP+>\t增大字体大小"
+
+#: gitk:3106
+#, tcl-format
+msgid "<%s-plus>\tIncrease font size"
+msgstr "<%s-plus>\t增大字体大小"
+
+#: gitk:3107
+#, tcl-format
+msgid "<%s-KP->\tDecrease font size"
+msgstr "<%s-KP->\t减小字体大小"
+
+#: gitk:3108
+#, tcl-format
+msgid "<%s-minus>\tDecrease font size"
+msgstr "<%s-minus>\t减小字体大小"
+
+#: gitk:3109
+msgid "<F5>\t\tUpdate"
+msgstr "<F5>\t\t更新"
+
+#: gitk:3574 gitk:3583
+#, tcl-format
+msgid "Error creating temporary directory %s:"
+msgstr "创建临时目录出错%s:"
+
+#: gitk:3596
+#, tcl-format
+msgid "Error getting \"%s\" from %s:"
+msgstr "从%s获取\"%s\"出错:"
+
+#: gitk:3659
+msgid "command failed:"
+msgstr "执行命令失败:"
+
+#: gitk:3808
+msgid "No such commit"
+msgstr "无此提交"
+
+#: gitk:3822
+msgid "git gui blame: command failed:"
+msgstr "git gui blame:执行命令失败:"
+
+#: gitk:3853
+#, tcl-format
+msgid "Couldn't read merge head: %s"
+msgstr "不能读取合并头(merge head):%s"
+
+#: gitk:3861
+#, tcl-format
+msgid "Error reading index: %s"
+msgstr "读取索引出错:%s"
+
+#: gitk:3886
+#, tcl-format
+msgid "Couldn't start git blame: %s"
+msgstr "不能执行git blame:%s"
+
+#: gitk:3889 gitk:6778
+msgid "Searching"
+msgstr "搜索中"
+
+#: gitk:3921
+#, tcl-format
+msgid "Error running git blame: %s"
+msgstr "运行git blame出错:%s"
+
+#: gitk:3949
+#, tcl-format
+msgid "That line comes from commit %s,  which is not in this view"
+msgstr "此行来自提交%s,不在此视图中"
+
+#: gitk:3963
+msgid "External diff viewer failed:"
+msgstr "外部diff查看器失败:"
+
+#: gitk:4067
+msgid "All files"
+msgstr "所有文件"
+
+#: gitk:4091
+msgid "View"
+msgstr "视图"
+
+#: gitk:4094
+msgid "Gitk view definition"
+msgstr "Gitk视图定义"
+
+#: gitk:4098
+msgid "Remember this view"
+msgstr "记住此视图"
+
+#: gitk:4099
+msgid "References (space separated list):"
+msgstr "引用(空格切分的列表):"
+
+#: gitk:4100
+msgid "Branches & tags:"
+msgstr "分支和tags"
+
+#: gitk:4101
+msgid "All refs"
+msgstr "所有引用"
+
+#: gitk:4102
+msgid "All (local) branches"
+msgstr "所有(本地)分支"
+
+#: gitk:4103
+msgid "All tags"
+msgstr "所有tag"
+
+#: gitk:4104
+msgid "All remote-tracking branches"
+msgstr "所有远程跟踪分支"
+
+#: gitk:4105
+msgid "Commit Info (regular expressions):"
+msgstr "提交信息 (正则表达式):"
+
+#: gitk:4106
+msgid "Author:"
+msgstr "作者:"
+
+#: gitk:4107
+msgid "Committer:"
+msgstr "提交者:"
+
+#: gitk:4108
+msgid "Commit Message:"
+msgstr "提交信息:"
+
+#: gitk:4109
+msgid "Matches all Commit Info criteria"
+msgstr "匹配所有提交信息标准"
+
+#: gitk:4110
+msgid "Matches no Commit Info criteria"
+msgstr "匹配无提交信息标准"
+
+#: gitk:4111
+msgid "Changes to Files:"
+msgstr "文件修改列表:"
+
+#: gitk:4112
+msgid "Fixed String"
+msgstr "固定字符串"
+
+#: gitk:4113
+msgid "Regular Expression"
+msgstr "正则表达式:"
+
+#: gitk:4114
+msgid "Search string:"
+msgstr "搜索字符串:"
+
+#: gitk:4115
+msgid ""
+"Commit Dates (\"2 weeks ago\", \"2009-03-17 15:27:38\", \"March 17, 2009 "
+"15:27:38\"):"
+msgstr "提交日期 (\"2星期之前\", \"2009-03-17 15:27:38\", \"5月 17, 2009 15:27:38\"):"
+
+#: gitk:4116
+msgid "Since:"
+msgstr "自:"
+
+#: gitk:4117
+msgid "Until:"
+msgstr "到:"
+
+#: gitk:4118
+msgid "Limit and/or skip a number of revisions (positive integer):"
+msgstr "限制 且/或 跳过一定数量的版本(正整数):"
+
+#: gitk:4119
+msgid "Number to show:"
+msgstr "显示数量:"
+
+#: gitk:4120
+msgid "Number to skip:"
+msgstr "跳过数量:"
+
+#: gitk:4121
+msgid "Miscellaneous options:"
+msgstr "其他选项:"
+
+#: gitk:4122
+msgid "Strictly sort by date"
+msgstr "严格按日期整理"
+
+#: gitk:4123
+msgid "Mark branch sides"
+msgstr "标记分支边界"
+
+#: gitk:4124
+msgid "Limit to first parent"
+msgstr "限制到第一个父提交"
+
+#: gitk:4125
+msgid "Simple history"
+msgstr "简易历史"
+
+#: gitk:4126
+msgid "Additional arguments to git log:"
+msgstr "git log命令的额外参数:"
+
+#: gitk:4127
+msgid "Enter files and directories to include, one per line:"
+msgstr "输入文件和文件夹来引用,每行一个:"
+
+#: gitk:4128
+msgid "Command to generate more commits to include:"
+msgstr "命令产生更多的提交来引用:"
+
+#: gitk:4252
+msgid "Gitk: edit view"
+msgstr "Gitk: 编辑视图"
+
+#: gitk:4260
+msgid "-- criteria for selecting revisions"
+msgstr "-- 用来选择版本的规则"
+
+#: gitk:4265
+msgid "View Name"
+msgstr "视图名称"
+
+#: gitk:4340
+msgid "Apply (F5)"
+msgstr "应用(F5)"
+
+#: gitk:4378
+msgid "Error in commit selection arguments:"
+msgstr "提交选择参数错误:"
+
+#: gitk:4433 gitk:4486 gitk:4948 gitk:4962 gitk:6232 gitk:12524 gitk:12525
+msgid "None"
+msgstr "无"
+
+#: gitk:5045 gitk:5050
+msgid "Descendant"
+msgstr "子提交"
+
+#: gitk:5046
+msgid "Not descendant"
+msgstr "非子提交"
+
+#: gitk:5053 gitk:5058
+msgid "Ancestor"
+msgstr "父提交"
+
+#: gitk:5054
+msgid "Not ancestor"
+msgstr "非父提交"
+
+#: gitk:5348
+msgid "Local changes checked in to index but not committed"
+msgstr "已添加到索引但未提交的修改"
+
+#: gitk:5384
+msgid "Local uncommitted changes, not checked in to index"
+msgstr "未添加到索引且未提交的修改"
+
+#: gitk:7158
+msgid "and many more"
+msgstr "更多"
+
+#: gitk:7161
+msgid "many"
+msgstr "很多"
+
+#: gitk:7352
+msgid "Tags:"
+msgstr "Tags:"
+
+#: gitk:7369 gitk:7375 gitk:8854
+msgid "Parent"
+msgstr "父节点"
+
+#: gitk:7380
+msgid "Child"
+msgstr "子节点"
+
+#: gitk:7389
+msgid "Branch"
+msgstr "分支"
+
+#: gitk:7392
+msgid "Follows"
+msgstr "之后的tag"
+
+#: gitk:7395
+msgid "Precedes"
+msgstr "之前的tag"
+
+#: gitk:7990
+#, tcl-format
+msgid "Error getting diffs: %s"
+msgstr "获取差异错误:%s"
+
+#: gitk:8679
+msgid "Goto:"
+msgstr "转到:"
+
+#: gitk:8700
+#, tcl-format
+msgid "Short SHA1 id %s is ambiguous"
+msgstr "短格式的SHA1提交号%s不明确、有歧义"
+
+#: gitk:8707
+#, tcl-format
+msgid "Revision %s is not known"
+msgstr "版本%s未知"
+
+#: gitk:8717
+#, tcl-format
+msgid "SHA1 id %s is not known"
+msgstr "提交号(SHA1 id)%s未知"
+
+#: gitk:8719
+#, tcl-format
+msgid "Revision %s is not in the current view"
+msgstr "版本%s不在当前视图中"
+
+#: gitk:8861 gitk:8876
+msgid "Date"
+msgstr "日期"
+
+#: gitk:8864
+msgid "Children"
+msgstr "子节点"
+
+#: gitk:8927
+#, tcl-format
+msgid "Reset %s branch to here"
+msgstr "重置分支%s到此处"
+
+#: gitk:8929
+msgid "Detached head: can't reset"
+msgstr "分离的头(head):不能重置(reset)"
+
+#: gitk:9034 gitk:9040
+msgid "Skipping merge commit "
+msgstr "跳过合并提交"
+
+#: gitk:9049 gitk:9054
+msgid "Error getting patch ID for "
+msgstr "获取补丁ID出错"
+
+#: gitk:9050 gitk:9055
+msgid " - stopping\n"
+msgstr " — 停止中\n"
+
+#: gitk:9060 gitk:9063 gitk:9071 gitk:9085 gitk:9094
+msgid "Commit "
+msgstr "提交"
+
+#: gitk:9064
+msgid ""
+" is the same patch as\n"
+"       "
+msgstr " 是相同的补丁(patch)\n       "
+
+#: gitk:9072
+msgid ""
+" differs from\n"
+"       "
+msgstr " 差异来自\n       "
+
+#: gitk:9074
+msgid ""
+"Diff of commits:\n"
+"\n"
+msgstr "提交的差异(Diff):\n\n"
+
+#: gitk:9086 gitk:9095
+#, tcl-format
+msgid " has %s children - stopping\n"
+msgstr "有%s子节点 — 停止中\n"
+
+#: gitk:9114
+#, tcl-format
+msgid "Error writing commit to file: %s"
+msgstr "写入提交到文件出错:%s"
+
+#: gitk:9120
+#, tcl-format
+msgid "Error diffing commits: %s"
+msgstr "比较提交差异出错:%s"
+
+#: gitk:9166
+msgid "Top"
+msgstr "顶部"
+
+#: gitk:9167
+msgid "From"
+msgstr "从"
+
+#: gitk:9172
+msgid "To"
+msgstr "到"
+
+#: gitk:9196
+msgid "Generate patch"
+msgstr "生成补丁(patch)"
+
+#: gitk:9198
+msgid "From:"
+msgstr "从:"
+
+#: gitk:9207
+msgid "To:"
+msgstr "到:"
+
+#: gitk:9216
+msgid "Reverse"
+msgstr "反向(Reverse)"
+
+#: gitk:9218 gitk:9428
+msgid "Output file:"
+msgstr "输出文件:"
+
+#: gitk:9224
+msgid "Generate"
+msgstr "生成"
+
+#: gitk:9262
+msgid "Error creating patch:"
+msgstr "创建补丁(patch)出错:"
+
+#: gitk:9285 gitk:9416 gitk:9504
+msgid "ID:"
+msgstr "ID:"
+
+#: gitk:9294
+msgid "Tag name:"
+msgstr "Tag名称:"
+
+#: gitk:9297
+msgid "Tag message is optional"
+msgstr "Tag信息是可选的"
+
+#: gitk:9299
+msgid "Tag message:"
+msgstr "Tag信息:"
+
+#: gitk:9303 gitk:9474
+msgid "Create"
+msgstr "创建"
+
+#: gitk:9321
+msgid "No tag name specified"
+msgstr "未指定tag名称"
+
+#: gitk:9325
+#, tcl-format
+msgid "Tag \"%s\" already exists"
+msgstr "Tag\"%s\"已经存在"
+
+#: gitk:9335
+msgid "Error creating tag:"
+msgstr "创建tag出错:"
+
+#: gitk:9425
+msgid "Command:"
+msgstr "命令:"
+
+#: gitk:9433
+msgid "Write"
+msgstr "写入"
+
+#: gitk:9451
+msgid "Error writing commit:"
+msgstr "写入提交出错:"
+
+#: gitk:9473
+msgid "Create branch"
+msgstr "创建分支"
+
+#: gitk:9489
+#, tcl-format
+msgid "Rename branch %s"
+msgstr "重命名分支%s"
+
+#: gitk:9490
+msgid "Rename"
+msgstr "重命名"
+
+#: gitk:9514
+msgid "Name:"
+msgstr "名称:"
+
+#: gitk:9538
+msgid "Please specify a name for the new branch"
+msgstr "请指定新分支的名称"
+
+#: gitk:9543
+#, tcl-format
+msgid "Branch '%s' already exists. Overwrite?"
+msgstr "分支\"%s\"已经存在。覆盖它?"
+
+#: gitk:9587
+msgid "Please specify a new name for the branch"
+msgstr "请重新指定新分支的名称"
+
+#: gitk:9650
+#, tcl-format
+msgid "Commit %s is already included in branch %s -- really re-apply it?"
+msgstr "提交%s已经存在于分支%s。确定重新应用它?"
+
+#: gitk:9655
+msgid "Cherry-picking"
+msgstr "打补丁中(Cherry-picking)"
+
+#: gitk:9664
+#, tcl-format
+msgid ""
+"Cherry-pick failed because of local changes to file '%s'.\n"
+"Please commit, reset or stash your changes and try again."
+msgstr "打补丁(Cherry-pick)失败,因为本地修改了文件\"%s\"。\n请提交(commit)、重置(reset)或暂存(stash)修改后重试。"
+
+#: gitk:9670
+msgid ""
+"Cherry-pick failed because of merge conflict.\n"
+"Do you wish to run git citool to resolve it?"
+msgstr "打补丁(Cherry-pick)失败因为合并冲突。\n你是否希望运行git citool 来解决冲突?"
+
+#: gitk:9686 gitk:9744
+msgid "No changes committed"
+msgstr "无已经提交的修改"
+
+#: gitk:9713
+#, tcl-format
+msgid "Commit %s is not included in branch %s -- really revert it?"
+msgstr "提交%s不包含在分支%s中,确认回滚(revert)它?"
+
+#: gitk:9718
+msgid "Reverting"
+msgstr "回滚中(Reverting)"
+
+#: gitk:9726
+#, tcl-format
+msgid ""
+"Revert failed because of local changes to the following files:%s Please "
+"commit, reset or stash  your changes and try again."
+msgstr "回滚(revert)失败,因为如下的本地文件修改:%s\n请提交(commit)、重置(reset)或者暂存(stash)改变后重试。"
+
+#: gitk:9730
+msgid ""
+"Revert failed because of merge conflict.\n"
+" Do you wish to run git citool to resolve it?"
+msgstr "回滚(revert)失败,因为合并冲突。\n你是否希望运行git citool来解决冲突?"
+
+#: gitk:9773
+msgid "Confirm reset"
+msgstr "确认重置(reset)"
+
+#: gitk:9775
+#, tcl-format
+msgid "Reset branch %s to %s?"
+msgstr "重置(reset)分支%s到%s?"
+
+#: gitk:9777
+msgid "Reset type:"
+msgstr "重置(reset)类型:"
+
+#: gitk:9780
+msgid "Soft: Leave working tree and index untouched"
+msgstr "软性:离开工作树,索引未改变"
+
+#: gitk:9783
+msgid "Mixed: Leave working tree untouched, reset index"
+msgstr "混合:离开工作树(未改变),索引重置"
+
+#: gitk:9786
+msgid ""
+"Hard: Reset working tree and index\n"
+"(discard ALL local changes)"
+msgstr "硬性:重置工作树和索引\n(丢弃所有的本地修改)"
+
+#: gitk:9803
+msgid "Resetting"
+msgstr "重置中(Resetting)"
+
+#: gitk:9876
+#, tcl-format
+msgid "A local branch named %s exists already"
+msgstr "本地分支%s已经存在"
+
+#: gitk:9884
+msgid "Checking out"
+msgstr "检出中(Checking out)"
+
+#: gitk:9943
+msgid "Cannot delete the currently checked-out branch"
+msgstr "不能删除当前检出(checkout)分支"
+
+#: gitk:9949
+#, tcl-format
+msgid ""
+"The commits on branch %s aren't on any other branch.\n"
+"Really delete branch %s?"
+msgstr "在分支%s上的提交不在其他任何分支上。\n确认删除分支%s?"
+
+#: gitk:9980
+#, tcl-format
+msgid "Tags and heads: %s"
+msgstr "Tags和头指针(heads):%s"
+
+#: gitk:9997
+msgid "Filter"
+msgstr "过滤器"
+
+#: gitk:10293
+msgid ""
+"Error reading commit topology information; branch and preceding/following "
+"tag information will be incomplete."
+msgstr "读取提交拓扑信息出错;分支和之前/之后的tag信息将不能完成。"
+
+#: gitk:11270
+msgid "Tag"
+msgstr "标签(Tag)"
+
+#: gitk:11274
+msgid "Id"
+msgstr "Id"
+
+#: gitk:11357
+msgid "Gitk font chooser"
+msgstr "Gitk字体选择"
+
+#: gitk:11374
+msgid "B"
+msgstr "粗体"
+
+#: gitk:11377
+msgid "I"
+msgstr "斜体"
+
+#: gitk:11495
+msgid "Commit list display options"
+msgstr "提交列表展示选项"
+
+#: gitk:11498
+msgid "Maximum graph width (lines)"
+msgstr "最大图宽度(行数)"
+
+#: gitk:11502
+#, no-tcl-format
+msgid "Maximum graph width (% of pane)"
+msgstr "最大图宽度(%窗口百分比)"
+
+#: gitk:11505
+msgid "Show local changes"
+msgstr "显示本地修改"
+
+#: gitk:11508
+msgid "Auto-select SHA1 (length)"
+msgstr "自动选择SHA1(长度)"
+
+#: gitk:11512
+msgid "Hide remote refs"
+msgstr "隐藏远程引用"
+
+#: gitk:11516
+msgid "Diff display options"
+msgstr "差异(Diff)展示选项"
+
+#: gitk:11518
+msgid "Tab spacing"
+msgstr "制表符宽度"
+
+#: gitk:11521
+msgid "Display nearby tags/heads"
+msgstr "显示临近的tags/heads"
+
+#: gitk:11524
+msgid "Maximum # tags/heads to show"
+msgstr "最大tags/heads展示数量"
+
+#: gitk:11527
+msgid "Limit diffs to listed paths"
+msgstr "diff中列出文件限制"
+
+#: gitk:11530
+msgid "Support per-file encodings"
+msgstr "单独文件编码支持"
+
+#: gitk:11536 gitk:11683
+msgid "External diff tool"
+msgstr "外部差异(diff)工具"
+
+#: gitk:11537
+msgid "Choose..."
+msgstr "选择..."
+
+#: gitk:11542
+msgid "General options"
+msgstr "常规选项"
+
+#: gitk:11545
+msgid "Use themed widgets"
+msgstr "使用主题小部件"
+
+#: gitk:11547
+msgid "(change requires restart)"
+msgstr "(需重启生效)"
+
+#: gitk:11549
+msgid "(currently unavailable)"
+msgstr "(当前不可用)"
+
+#: gitk:11560
+msgid "Colors: press to choose"
+msgstr "颜色:点击来选择"
+
+#: gitk:11563
+msgid "Interface"
+msgstr "界面"
+
+#: gitk:11564
+msgid "interface"
+msgstr "界面"
+
+#: gitk:11567
+msgid "Background"
+msgstr "背景"
+
+#: gitk:11568 gitk:11598
+msgid "background"
+msgstr "背景"
+
+#: gitk:11571
+msgid "Foreground"
+msgstr "前景"
+
+#: gitk:11572
+msgid "foreground"
+msgstr "前景"
+
+#: gitk:11575
+msgid "Diff: old lines"
+msgstr "差异(Diff):老代码行"
+
+#: gitk:11576
+msgid "diff old lines"
+msgstr "差异(diff)老代码行"
+
+#: gitk:11580
+msgid "Diff: new lines"
+msgstr "差异(Diff):新代码行"
+
+#: gitk:11581
+msgid "diff new lines"
+msgstr "差异(diff)新代码行"
+
+#: gitk:11585
+msgid "Diff: hunk header"
+msgstr "差异(Diff):补丁片段头信息"
+
+#: gitk:11587
+msgid "diff hunk header"
+msgstr "差异(diff)补丁片段头信息"
+
+#: gitk:11591
+msgid "Marked line bg"
+msgstr "已标记代码行背景"
+
+#: gitk:11593
+msgid "marked line background"
+msgstr "已标记代码行背景"
+
+#: gitk:11597
+msgid "Select bg"
+msgstr "选择背景"
+
+#: gitk:11606
+msgid "Fonts: press to choose"
+msgstr "字体:点击来选择"
+
+#: gitk:11608
+msgid "Main font"
+msgstr "主字体"
+
+#: gitk:11609
+msgid "Diff display font"
+msgstr "差异(Diff)显示字体"
+
+#: gitk:11610
+msgid "User interface font"
+msgstr "用户界面字体"
+
+#: gitk:11632
+msgid "Gitk preferences"
+msgstr "Gitk偏好设置"
+
+#: gitk:11641
+msgid "General"
+msgstr "常规"
+
+#: gitk:11642
+msgid "Colors"
+msgstr "颜色"
+
+#: gitk:11643
+msgid "Fonts"
+msgstr "字体"
+
+#: gitk:11693
+#, tcl-format
+msgid "Gitk: choose color for %s"
+msgstr "Gitk:选择颜色用于%s"
+
+#: gitk:12206
+msgid ""
+"Sorry, gitk cannot run with this version of Tcl/Tk.\n"
+" Gitk requires at least Tcl/Tk 8.4."
+msgstr "对不起,gitk不能运行在当前版本的Tcl/Tk中。\nGitk运行需要最低版本为Tcl/Tk8.4。"
+
+#: gitk:12416
+msgid "Cannot find a git repository here."
+msgstr "在此位置未发现git仓库。"
+
+#: gitk:12463
+#, tcl-format
+msgid "Ambiguous argument '%s': both revision and filename"
+msgstr "不明确有歧义的参数\"%s\":版本和文件名称"
+
+#: gitk:12475
+msgid "Bad arguments to gitk:"
+msgstr "运行gitk参数错误:"
diff --git a/http.c b/http.c
index 27aa0a3192988cd0c272dab0e9f6cf52d538b6fa..938b9e55af435c46484a4fd54747a9c0bc2db0fe 100644 (file)
--- a/http.c
+++ b/http.c
@@ -513,9 +513,11 @@ static void set_proxyauth_name_password(CURL *result)
 #else
                struct strbuf s = STRBUF_INIT;
 
-               strbuf_addstr_urlencode(&s, proxy_auth.username, 1);
+               strbuf_addstr_urlencode(&s, proxy_auth.username,
+                                       is_rfc3986_unreserved);
                strbuf_addch(&s, ':');
-               strbuf_addstr_urlencode(&s, proxy_auth.password, 1);
+               strbuf_addstr_urlencode(&s, proxy_auth.password,
+                                       is_rfc3986_unreserved);
                curl_proxyuserpwd = strbuf_detach(&s, NULL);
                curl_easy_setopt(result, CURLOPT_PROXYUSERPWD, curl_proxyuserpwd);
 #endif
diff --git a/http.h b/http.h
index b429f1cf042bbb61e97e41cd1aacb012c4db834e..5e0ad724f92f3c708d2b330bb689f6eef6bb8de4 100644 (file)
--- a/http.h
+++ b/http.h
 #if LIBCURL_VERSION_NUM < 0x070704
 #define curl_global_cleanup() do { /* nothing */ } while (0)
 #endif
+
 #if LIBCURL_VERSION_NUM < 0x070800
 #define curl_global_init(a) do { /* nothing */ } while (0)
+#elif LIBCURL_VERSION_NUM >= 0x070c00
+#define curl_global_init(a) curl_global_init_mem(a, xmalloc, free, \
+                                               xrealloc, xstrdup, xcalloc)
 #endif
 
 #if (LIBCURL_VERSION_NUM < 0x070c04) || (LIBCURL_VERSION_NUM == 0x071000)
index 3aff1849e7d5c7dba5c14b2fa34f7af64064a42d..9010e00950b379501a6607d660497e94f8412d95 100644 (file)
@@ -737,6 +737,38 @@ static struct line_log_data *lookup_line_range(struct rev_info *revs,
        return ret;
 }
 
+static int same_paths_in_pathspec_and_range(struct pathspec *pathspec,
+                                           struct line_log_data *range)
+{
+       int i;
+       struct line_log_data *r;
+
+       for (i = 0, r = range; i < pathspec->nr && r; i++, r = r->next)
+               if (strcmp(pathspec->items[i].match, r->path))
+                       return 0;
+       if (i < pathspec->nr || r)
+               /* different number of pathspec items and ranges */
+               return 0;
+
+       return 1;
+}
+
+static void parse_pathspec_from_ranges(struct pathspec *pathspec,
+                                      struct line_log_data *range)
+{
+       struct line_log_data *r;
+       struct argv_array array = ARGV_ARRAY_INIT;
+       const char **paths;
+
+       for (r = range; r; r = r->next)
+               argv_array_push(&array, r->path);
+       paths = argv_array_detach(&array);
+
+       parse_pathspec(pathspec, 0, PATHSPEC_PREFER_FULL, "", paths);
+       /* strings are now owned by pathspec */
+       free(paths);
+}
+
 void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args)
 {
        struct commit *commit = NULL;
@@ -746,20 +778,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
        range = parse_lines(rev->diffopt.repo, commit, prefix, args);
        add_line_range(rev, commit, range);
 
-       if (!rev->diffopt.detect_rename) {
-               struct line_log_data *r;
-               struct argv_array array = ARGV_ARRAY_INIT;
-               const char **paths;
-
-               for (r = range; r; r = r->next)
-                       argv_array_push(&array, r->path);
-               paths = argv_array_detach(&array);
-
-               parse_pathspec(&rev->diffopt.pathspec, 0,
-                              PATHSPEC_PREFER_FULL, "", paths);
-               /* strings are now owned by pathspec */
-               free(paths);
-       }
+       parse_pathspec_from_ranges(&rev->diffopt.pathspec, range);
 }
 
 static void move_diff_queue(struct diff_queue_struct *dst,
@@ -817,15 +836,29 @@ static void queue_diffs(struct line_log_data *range,
                        struct diff_queue_struct *queue,
                        struct commit *commit, struct commit *parent)
 {
+       struct object_id *tree_oid, *parent_tree_oid;
+
        assert(commit);
 
+       tree_oid = get_commit_tree_oid(commit);
+       parent_tree_oid = parent ? get_commit_tree_oid(parent) : NULL;
+
+       if (opt->detect_rename &&
+           !same_paths_in_pathspec_and_range(&opt->pathspec, range)) {
+               clear_pathspec(&opt->pathspec);
+               parse_pathspec_from_ranges(&opt->pathspec, range);
+       }
        DIFF_QUEUE_CLEAR(&diff_queued_diff);
-       diff_tree_oid(parent ? get_commit_tree_oid(parent) : NULL,
-                     get_commit_tree_oid(commit), "", opt);
-       if (opt->detect_rename) {
+       diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
+       if (opt->detect_rename && diff_might_be_rename()) {
+               /* must look at the full tree diff to detect renames */
+               clear_pathspec(&opt->pathspec);
+               DIFF_QUEUE_CLEAR(&diff_queued_diff);
+
+               diff_tree_oid(parent_tree_oid, tree_oid, "", opt);
+
                filter_diffs_for_paths(range, 1);
-               if (diff_might_be_rename())
-                       diffcore_std(opt);
+               diffcore_std(opt);
                filter_diffs_for_paths(range, 0);
        }
        move_diff_queue(queue, &diff_queued_diff);
index 1cb20c659c82b151a652da0528d0673ac629cc6c..4d88bfe64ad230b4055a77b79432ae2db8af87f6 100644 (file)
@@ -6,6 +6,14 @@
 #include "list-objects.h"
 #include "list-objects-filter.h"
 #include "list-objects-filter-options.h"
+#include "promisor-remote.h"
+#include "trace.h"
+#include "url.h"
+
+static int parse_combine_filter(
+       struct list_objects_filter_options *filter_options,
+       const char *arg,
+       struct strbuf *errbuf);
 
 /*
  * Parse value of the argument to the "filter" keyword.
@@ -29,16 +37,11 @@ static int gently_parse_list_objects_filter(
 {
        const char *v0;
 
-       if (filter_options->choice) {
-               if (errbuf) {
-                       strbuf_addstr(
-                               errbuf,
-                               _("multiple filter-specs cannot be combined"));
-               }
-               return 1;
-       }
+       if (!arg)
+               return 0;
 
-       filter_options->filter_spec = strdup(arg);
+       if (filter_options->choice)
+               BUG("filter_options already populated");
 
        if (!strcmp(arg, "blob:none")) {
                filter_options->choice = LOFC_BLOB_NONE;
@@ -52,11 +55,7 @@ static int gently_parse_list_objects_filter(
 
        } else if (skip_prefix(arg, "tree:", &v0)) {
                if (!git_parse_ulong(v0, &filter_options->tree_exclude_depth)) {
-                       if (errbuf) {
-                               strbuf_addstr(
-                                       errbuf,
-                                       _("expected 'tree:<depth>'"));
-                       }
+                       strbuf_addstr(errbuf, _("expected 'tree:<depth>'"));
                        return 1;
                }
                filter_options->choice = LOFC_TREE_DEPTH;
@@ -84,103 +83,298 @@ static int gently_parse_list_objects_filter(
                                _("sparse:path filters support has been dropped"));
                }
                return 1;
+
+       } else if (skip_prefix(arg, "combine:", &v0)) {
+               return parse_combine_filter(filter_options, v0, errbuf);
+
        }
        /*
         * Please update _git_fetch() in git-completion.bash when you
         * add new filters
         */
 
-       if (errbuf)
-               strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg);
+       strbuf_addf(errbuf, _("invalid filter-spec '%s'"), arg);
 
        memset(filter_options, 0, sizeof(*filter_options));
        return 1;
 }
 
-int parse_list_objects_filter(struct list_objects_filter_options *filter_options,
-                             const char *arg)
+static const char *RESERVED_NON_WS = "~`!@#$^&*()[]{}\\;'\",<>?";
+
+static int has_reserved_character(
+       struct strbuf *sub_spec, struct strbuf *errbuf)
 {
-       struct strbuf buf = STRBUF_INIT;
-       if (gently_parse_list_objects_filter(filter_options, arg, &buf))
-               die("%s", buf.buf);
+       const char *c = sub_spec->buf;
+       while (*c) {
+               if (*c <= ' ' || strchr(RESERVED_NON_WS, *c)) {
+                       strbuf_addf(
+                               errbuf,
+                               _("must escape char in sub-filter-spec: '%c'"),
+                               *c);
+                       return 1;
+               }
+               c++;
+       }
+
        return 0;
 }
 
+static int parse_combine_subfilter(
+       struct list_objects_filter_options *filter_options,
+       struct strbuf *subspec,
+       struct strbuf *errbuf)
+{
+       size_t new_index = filter_options->sub_nr;
+       char *decoded;
+       int result;
+
+       ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1,
+                     filter_options->sub_alloc);
+
+       decoded = url_percent_decode(subspec->buf);
+
+       result = has_reserved_character(subspec, errbuf) ||
+               gently_parse_list_objects_filter(
+                       &filter_options->sub[new_index], decoded, errbuf);
+
+       free(decoded);
+       return result;
+}
+
+static int parse_combine_filter(
+       struct list_objects_filter_options *filter_options,
+       const char *arg,
+       struct strbuf *errbuf)
+{
+       struct strbuf **subspecs = strbuf_split_str(arg, '+', 0);
+       size_t sub;
+       int result = 0;
+
+       if (!subspecs[0]) {
+               strbuf_addstr(errbuf, _("expected something after combine:"));
+               result = 1;
+               goto cleanup;
+       }
+
+       for (sub = 0; subspecs[sub] && !result; sub++) {
+               if (subspecs[sub + 1]) {
+                       /*
+                        * This is not the last subspec. Remove trailing "+" so
+                        * we can parse it.
+                        */
+                       size_t last = subspecs[sub]->len - 1;
+                       assert(subspecs[sub]->buf[last] == '+');
+                       strbuf_remove(subspecs[sub], last, 1);
+               }
+               result = parse_combine_subfilter(
+                       filter_options, subspecs[sub], errbuf);
+       }
+
+       filter_options->choice = LOFC_COMBINE;
+
+cleanup:
+       strbuf_list_free(subspecs);
+       if (result) {
+               list_objects_filter_release(filter_options);
+               memset(filter_options, 0, sizeof(*filter_options));
+       }
+       return result;
+}
+
+static int allow_unencoded(char ch)
+{
+       if (ch <= ' ' || ch == '%' || ch == '+')
+               return 0;
+       return !strchr(RESERVED_NON_WS, ch);
+}
+
+static void filter_spec_append_urlencode(
+       struct list_objects_filter_options *filter, const char *raw)
+{
+       struct strbuf buf = STRBUF_INIT;
+       strbuf_addstr_urlencode(&buf, raw, allow_unencoded);
+       trace_printf("Add to combine filter-spec: %s\n", buf.buf);
+       string_list_append(&filter->filter_spec, strbuf_detach(&buf, NULL));
+}
+
+/*
+ * Changes filter_options into an equivalent LOFC_COMBINE filter options
+ * instance. Does not do anything if filter_options is already LOFC_COMBINE.
+ */
+static void transform_to_combine_type(
+       struct list_objects_filter_options *filter_options)
+{
+       assert(filter_options->choice);
+       if (filter_options->choice == LOFC_COMBINE)
+               return;
+       {
+               const int initial_sub_alloc = 2;
+               struct list_objects_filter_options *sub_array =
+                       xcalloc(initial_sub_alloc, sizeof(*sub_array));
+               sub_array[0] = *filter_options;
+               memset(filter_options, 0, sizeof(*filter_options));
+               filter_options->sub = sub_array;
+               filter_options->sub_alloc = initial_sub_alloc;
+       }
+       filter_options->sub_nr = 1;
+       filter_options->choice = LOFC_COMBINE;
+       string_list_append(&filter_options->filter_spec, xstrdup("combine:"));
+       filter_spec_append_urlencode(
+               filter_options,
+               list_objects_filter_spec(&filter_options->sub[0]));
+       /*
+        * We don't need the filter_spec strings for subfilter specs, only the
+        * top level.
+        */
+       string_list_clear(&filter_options->sub[0].filter_spec, /*free_util=*/0);
+}
+
+void list_objects_filter_die_if_populated(
+       struct list_objects_filter_options *filter_options)
+{
+       if (filter_options->choice)
+               die(_("multiple filter-specs cannot be combined"));
+}
+
+void parse_list_objects_filter(
+       struct list_objects_filter_options *filter_options,
+       const char *arg)
+{
+       struct strbuf errbuf = STRBUF_INIT;
+       int parse_error;
+
+       if (!filter_options->choice) {
+               string_list_append(&filter_options->filter_spec, xstrdup(arg));
+
+               parse_error = gently_parse_list_objects_filter(
+                       filter_options, arg, &errbuf);
+       } else {
+               /*
+                * Make filter_options an LOFC_COMBINE spec so we can trivially
+                * add subspecs to it.
+                */
+               transform_to_combine_type(filter_options);
+
+               string_list_append(&filter_options->filter_spec, xstrdup("+"));
+               filter_spec_append_urlencode(filter_options, arg);
+               ALLOC_GROW_BY(filter_options->sub, filter_options->sub_nr, 1,
+                             filter_options->sub_alloc);
+
+               parse_error = gently_parse_list_objects_filter(
+                       &filter_options->sub[filter_options->sub_nr - 1], arg,
+                       &errbuf);
+       }
+       if (parse_error)
+               die("%s", errbuf.buf);
+}
+
 int opt_parse_list_objects_filter(const struct option *opt,
                                  const char *arg, int unset)
 {
        struct list_objects_filter_options *filter_options = opt->value;
 
-       if (unset || !arg) {
+       if (unset || !arg)
                list_objects_filter_set_no_filter(filter_options);
-               return 0;
+       else
+               parse_list_objects_filter(filter_options, arg);
+       return 0;
+}
+
+const char *list_objects_filter_spec(struct list_objects_filter_options *filter)
+{
+       if (!filter->filter_spec.nr)
+               BUG("no filter_spec available for this filter");
+       if (filter->filter_spec.nr != 1) {
+               struct strbuf concatted = STRBUF_INIT;
+               strbuf_add_separated_string_list(
+                       &concatted, "", &filter->filter_spec);
+               string_list_clear(&filter->filter_spec, /*free_util=*/0);
+               string_list_append(
+                       &filter->filter_spec, strbuf_detach(&concatted, NULL));
        }
 
-       return parse_list_objects_filter(filter_options, arg);
+       return filter->filter_spec.items[0].string;
 }
 
-void expand_list_objects_filter_spec(
-       const struct list_objects_filter_options *filter,
-       struct strbuf *expanded_spec)
+const char *expand_list_objects_filter_spec(
+       struct list_objects_filter_options *filter)
 {
-       strbuf_init(expanded_spec, strlen(filter->filter_spec));
-       if (filter->choice == LOFC_BLOB_LIMIT)
-               strbuf_addf(expanded_spec, "blob:limit=%lu",
+       if (filter->choice == LOFC_BLOB_LIMIT) {
+               struct strbuf expanded_spec = STRBUF_INIT;
+               strbuf_addf(&expanded_spec, "blob:limit=%lu",
                            filter->blob_limit_value);
-       else if (filter->choice == LOFC_TREE_DEPTH)
-               strbuf_addf(expanded_spec, "tree:%lu",
-                           filter->tree_exclude_depth);
-       else
-               strbuf_addstr(expanded_spec, filter->filter_spec);
+               string_list_clear(&filter->filter_spec, /*free_util=*/0);
+               string_list_append(
+                       &filter->filter_spec,
+                       strbuf_detach(&expanded_spec, NULL));
+       }
+
+       return list_objects_filter_spec(filter);
 }
 
 void list_objects_filter_release(
        struct list_objects_filter_options *filter_options)
 {
-       free(filter_options->filter_spec);
+       size_t sub;
+
+       if (!filter_options)
+               return;
+       string_list_clear(&filter_options->filter_spec, /*free_util=*/0);
        free(filter_options->sparse_oid_value);
+       for (sub = 0; sub < filter_options->sub_nr; sub++)
+               list_objects_filter_release(&filter_options->sub[sub]);
+       free(filter_options->sub);
        memset(filter_options, 0, sizeof(*filter_options));
 }
 
 void partial_clone_register(
        const char *remote,
-       const struct list_objects_filter_options *filter_options)
+       struct list_objects_filter_options *filter_options)
 {
-       /*
-        * Record the name of the partial clone remote in the
-        * config and in the global variable -- the latter is
-        * used throughout to indicate that partial clone is
-        * enabled and to expect missing objects.
-        */
-       if (repository_format_partial_clone &&
-           *repository_format_partial_clone &&
-           strcmp(remote, repository_format_partial_clone))
-               die(_("cannot change partial clone promisor remote"));
+       char *cfg_name;
+       char *filter_name;
 
-       git_config_set("core.repositoryformatversion", "1");
-       git_config_set("extensions.partialclone", remote);
+       /* Check if it is already registered */
+       if (!promisor_remote_find(remote)) {
+               git_config_set("core.repositoryformatversion", "1");
 
-       repository_format_partial_clone = xstrdup(remote);
+               /* Add promisor config for the remote */
+               cfg_name = xstrfmt("remote.%s.promisor", remote);
+               git_config_set(cfg_name, "true");
+               free(cfg_name);
+       }
 
        /*
         * Record the initial filter-spec in the config as
         * the default for subsequent fetches from this remote.
         */
-       core_partial_clone_filter_default =
-               xstrdup(filter_options->filter_spec);
-       git_config_set("core.partialclonefilter",
-                      core_partial_clone_filter_default);
+       filter_name = xstrfmt("remote.%s.partialclonefilter", remote);
+       /* NEEDSWORK: 'expand' result leaking??? */
+       git_config_set(filter_name,
+                      expand_list_objects_filter_spec(filter_options));
+       free(filter_name);
+
+       /* Make sure the config info are reset */
+       promisor_remote_reinit();
 }
 
 void partial_clone_get_default_filter_spec(
-       struct list_objects_filter_options *filter_options)
+       struct list_objects_filter_options *filter_options,
+       const char *remote)
 {
+       struct promisor_remote *promisor = promisor_remote_find(remote);
+       struct strbuf errbuf = STRBUF_INIT;
+
        /*
         * Parse default value, but silently ignore it if it is invalid.
         */
-       if (!core_partial_clone_filter_default)
+       if (!promisor)
                return;
+
+       string_list_append(&filter_options->filter_spec,
+                          promisor->partial_clone_filter);
        gently_parse_list_objects_filter(filter_options,
-                                        core_partial_clone_filter_default,
-                                        NULL);
+                                        promisor->partial_clone_filter,
+                                        &errbuf);
+       strbuf_release(&errbuf);
 }
index c54f0000fbade5608e81345a9c5dbd54137d352d..b63c5ee1a368aa4d34cda8bbc41fce905692495b 100644 (file)
@@ -2,7 +2,7 @@
 #define LIST_OBJECTS_FILTER_OPTIONS_H
 
 #include "parse-options.h"
-#include "strbuf.h"
+#include "string-list.h"
 
 /*
  * The list of defined filters for list-objects.
@@ -13,6 +13,7 @@ enum list_objects_filter_choice {
        LOFC_BLOB_LIMIT,
        LOFC_TREE_DEPTH,
        LOFC_SPARSE_OID,
+       LOFC_COMBINE,
        LOFC__COUNT /* must be last */
 };
 
@@ -23,8 +24,10 @@ struct list_objects_filter_options {
         * commands that launch filtering sub-processes, or for communication
         * over the network, don't use this value; use the result of
         * expand_list_objects_filter_spec() instead.
+        * To get the raw filter spec given by the user, use the result of
+        * list_objects_filter_spec().
         */
-       char *filter_spec;
+       struct string_list filter_spec;
 
        /*
         * 'choice' is determined by parsing the filter-spec.  This indicates
@@ -38,19 +41,40 @@ struct list_objects_filter_options {
        unsigned int no_filter : 1;
 
        /*
-        * Parsed values (fields) from within the filter-spec.  These are
-        * choice-specific; not all values will be defined for any given
-        * choice.
+        * BEGIN choice-specific parsed values from within the filter-spec. Only
+        * some values will be defined for any given choice.
         */
+
        struct object_id *sparse_oid_value;
        unsigned long blob_limit_value;
        unsigned long tree_exclude_depth;
+
+       /* LOFC_COMBINE values */
+
+       /* This array contains all the subfilters which this filter combines. */
+       size_t sub_nr, sub_alloc;
+       struct list_objects_filter_options *sub;
+
+       /*
+        * END choice-specific parsed values.
+        */
 };
 
 /* Normalized command line arguments */
 #define CL_ARG__FILTER "filter"
 
-int parse_list_objects_filter(
+void list_objects_filter_die_if_populated(
+       struct list_objects_filter_options *filter_options);
+
+/*
+ * Parses the filter spec string given by arg and either (1) simply places the
+ * result in filter_options if it is not yet populated or (2) combines it with
+ * the filter already in filter_options if it is already populated. In the case
+ * of (2), the filter specs are combined as if specified with 'combine:'.
+ *
+ * Dies and prints a user-facing message if an error occurs.
+ */
+void parse_list_objects_filter(
        struct list_objects_filter_options *filter_options,
        const char *arg);
 
@@ -65,13 +89,22 @@ int opt_parse_list_objects_filter(const struct option *opt,
 /*
  * Translates abbreviated numbers in the filter's filter_spec into their
  * fully-expanded forms (e.g., "limit:blob=1k" becomes "limit:blob=1024").
+ * Returns a string owned by the list_objects_filter_options object.
  *
- * This form should be used instead of the raw filter_spec field when
- * communicating with a remote process or subprocess.
+ * This form should be used instead of the raw list_objects_filter_spec()
+ * value when communicating with a remote process or subprocess.
+ */
+const char *expand_list_objects_filter_spec(
+       struct list_objects_filter_options *filter);
+
+/*
+ * Returns the filter spec string more or less in the form as the user
+ * entered it. This form of the filter_spec can be used in user-facing
+ * messages.  Returns a string owned by the list_objects_filter_options
+ * object.
  */
-void expand_list_objects_filter_spec(
-       const struct list_objects_filter_options *filter,
-       struct strbuf *expanded_spec);
+const char *list_objects_filter_spec(
+       struct list_objects_filter_options *filter);
 
 void list_objects_filter_release(
        struct list_objects_filter_options *filter_options);
@@ -85,8 +118,9 @@ static inline void list_objects_filter_set_no_filter(
 
 void partial_clone_register(
        const char *remote,
-       const struct list_objects_filter_options *filter_options);
-void partial_clone_get_default_filter_spec(
        struct list_objects_filter_options *filter_options);
+void partial_clone_get_default_filter_spec(
+       struct list_objects_filter_options *filter_options,
+       const char *remote);
 
 #endif /* LIST_OBJECTS_FILTER_OPTIONS_H */
index 36e1f774bcfc50d0475ad835464cec092314eb79..d664264d65947f140d04d25d25db54af23b4a2d3 100644 (file)
  */
 #define FILTER_SHOWN_BUT_REVISIT (1<<21)
 
-/*
- * A filter for list-objects to omit ALL blobs from the traversal.
- * And to OPTIONALLY collect a list of the omitted OIDs.
- */
-struct filter_blobs_none_data {
+struct subfilter {
+       struct filter *filter;
+       struct oidset seen;
+       struct oidset omits;
+       struct object_id skip_tree;
+       unsigned is_skipping_tree : 1;
+};
+
+struct filter {
+       enum list_objects_filter_result (*filter_object_fn)(
+               struct repository *r,
+               enum list_objects_filter_situation filter_situation,
+               struct object *obj,
+               const char *pathname,
+               const char *filename,
+               struct oidset *omits,
+               void *filter_data);
+
+       /*
+        * Optional. If this function is supplied and the filter needs
+        * to collect omits, then this function is called once before
+        * free_fn is called.
+        *
+        * This is required because the following two conditions hold:
+        *
+        *   a. A tree filter can add and remove objects as an object
+        *      graph is traversed.
+        *   b. A combine filter's omit set is the union of all its
+        *      subfilters, which may include tree: filters.
+        *
+        * As such, the omits sets must be separate sets, and can only
+        * be unioned after the traversal is completed.
+        */
+       void (*finalize_omits_fn)(struct oidset *omits, void *filter_data);
+
+       void (*free_fn)(void *filter_data);
+
+       void *filter_data;
+
+       /* If non-NULL, the filter collects a list of the omitted OIDs here. */
        struct oidset *omits;
 };
 
@@ -40,10 +75,9 @@ static enum list_objects_filter_result filter_blobs_none(
        struct object *obj,
        const char *pathname,
        const char *filename,
+       struct oidset *omits,
        void *filter_data_)
 {
-       struct filter_blobs_none_data *filter_data = filter_data_;
-
        switch (filter_situation) {
        default:
                BUG("unknown filter_situation: %d", filter_situation);
@@ -61,24 +95,18 @@ static enum list_objects_filter_result filter_blobs_none(
                assert(obj->type == OBJ_BLOB);
                assert((obj->flags & SEEN) == 0);
 
-               if (filter_data->omits)
-                       oidset_insert(filter_data->omits, &obj->oid);
+               if (omits)
+                       oidset_insert(omits, &obj->oid);
                return LOFR_MARK_SEEN; /* but not LOFR_DO_SHOW (hard omit) */
        }
 }
 
-static void *filter_blobs_none__init(
-       struct oidset *omitted,
+static void filter_blobs_none__init(
        struct list_objects_filter_options *filter_options,
-       filter_object_fn *filter_fn,
-       filter_free_fn *filter_free_fn)
+       struct filter *filter)
 {
-       struct filter_blobs_none_data *d = xcalloc(1, sizeof(*d));
-       d->omits = omitted;
-
-       *filter_fn = filter_blobs_none;
-       *filter_free_fn = free;
-       return d;
+       filter->filter_object_fn = filter_blobs_none;
+       filter->free_fn = free;
 }
 
 /*
@@ -86,8 +114,6 @@ static void *filter_blobs_none__init(
  * Can OPTIONALLY collect a list of the omitted OIDs.
  */
 struct filter_trees_depth_data {
-       struct oidset *omits;
-
        /*
         * Maps trees to the minimum depth at which they were seen. It is not
         * necessary to re-traverse a tree at deeper or equal depths than it has
@@ -110,16 +136,16 @@ struct seen_map_entry {
 /* Returns 1 if the oid was in the omits set before it was invoked. */
 static int filter_trees_update_omits(
        struct object *obj,
-       struct filter_trees_depth_data *filter_data,
+       struct oidset *omits,
        int include_it)
 {
-       if (!filter_data->omits)
+       if (!omits)
                return 0;
 
        if (include_it)
-               return oidset_remove(filter_data->omits, &obj->oid);
+               return oidset_remove(omits, &obj->oid);
        else
-               return oidset_insert(filter_data->omits, &obj->oid);
+               return oidset_insert(omits, &obj->oid);
 }
 
 static enum list_objects_filter_result filter_trees_depth(
@@ -128,6 +154,7 @@ static enum list_objects_filter_result filter_trees_depth(
        struct object *obj,
        const char *pathname,
        const char *filename,
+       struct oidset *omits,
        void *filter_data_)
 {
        struct filter_trees_depth_data *filter_data = filter_data_;
@@ -152,7 +179,7 @@ static enum list_objects_filter_result filter_trees_depth(
                return LOFR_ZERO;
 
        case LOFS_BLOB:
-               filter_trees_update_omits(obj, filter_data, include_it);
+               filter_trees_update_omits(obj, omits, include_it);
                return include_it ? LOFR_MARK_SEEN | LOFR_DO_SHOW : LOFR_ZERO;
 
        case LOFS_BEGIN_TREE:
@@ -173,12 +200,12 @@ static enum list_objects_filter_result filter_trees_depth(
                        filter_res = LOFR_SKIP_TREE;
                } else {
                        int been_omitted = filter_trees_update_omits(
-                               obj, filter_data, include_it);
+                               obj, omits, include_it);
                        seen_info->depth = filter_data->current_depth;
 
                        if (include_it)
                                filter_res = LOFR_DO_SHOW;
-                       else if (filter_data->omits && !been_omitted)
+                       else if (omits && !been_omitted)
                                /*
                                 * Must update omit information of children
                                 * recursively; they have not been omitted yet.
@@ -201,21 +228,18 @@ static void filter_trees_free(void *filter_data) {
        free(d);
 }
 
-static void *filter_trees_depth__init(
-       struct oidset *omitted,
+static void filter_trees_depth__init(
        struct list_objects_filter_options *filter_options,
-       filter_object_fn *filter_fn,
-       filter_free_fn *filter_free_fn)
+       struct filter *filter)
 {
        struct filter_trees_depth_data *d = xcalloc(1, sizeof(*d));
-       d->omits = omitted;
        oidmap_init(&d->seen_at_depth, 0);
        d->exclude_depth = filter_options->tree_exclude_depth;
        d->current_depth = 0;
 
-       *filter_fn = filter_trees_depth;
-       *filter_free_fn = filter_trees_free;
-       return d;
+       filter->filter_data = d;
+       filter->filter_object_fn = filter_trees_depth;
+       filter->free_fn = filter_trees_free;
 }
 
 /*
@@ -223,7 +247,6 @@ static void *filter_trees_depth__init(
  * And to OPTIONALLY collect a list of the omitted OIDs.
  */
 struct filter_blobs_limit_data {
-       struct oidset *omits;
        unsigned long max_bytes;
 };
 
@@ -233,6 +256,7 @@ static enum list_objects_filter_result filter_blobs_limit(
        struct object *obj,
        const char *pathname,
        const char *filename,
+       struct oidset *omits,
        void *filter_data_)
 {
        struct filter_blobs_limit_data *filter_data = filter_data_;
@@ -270,30 +294,27 @@ static enum list_objects_filter_result filter_blobs_limit(
                if (object_length < filter_data->max_bytes)
                        goto include_it;
 
-               if (filter_data->omits)
-                       oidset_insert(filter_data->omits, &obj->oid);
+               if (omits)
+                       oidset_insert(omits, &obj->oid);
                return LOFR_MARK_SEEN; /* but not LOFR_DO_SHOW (hard omit) */
        }
 
 include_it:
-       if (filter_data->omits)
-               oidset_remove(filter_data->omits, &obj->oid);
+       if (omits)
+               oidset_remove(omits, &obj->oid);
        return LOFR_MARK_SEEN | LOFR_DO_SHOW;
 }
 
-static void *filter_blobs_limit__init(
-       struct oidset *omitted,
+static void filter_blobs_limit__init(
        struct list_objects_filter_options *filter_options,
-       filter_object_fn *filter_fn,
-       filter_free_fn *filter_free_fn)
+       struct filter *filter)
 {
        struct filter_blobs_limit_data *d = xcalloc(1, sizeof(*d));
-       d->omits = omitted;
        d->max_bytes = filter_options->blob_limit_value;
 
-       *filter_fn = filter_blobs_limit;
-       *filter_free_fn = free;
-       return d;
+       filter->filter_data = d;
+       filter->filter_object_fn = filter_blobs_limit;
+       filter->free_fn = free;
 }
 
 /*
@@ -326,7 +347,6 @@ struct frame {
 };
 
 struct filter_sparse_data {
-       struct oidset *omits;
        struct exclude_list el;
 
        size_t nr, alloc;
@@ -339,6 +359,7 @@ static enum list_objects_filter_result filter_sparse(
        struct object *obj,
        const char *pathname,
        const char *filename,
+       struct oidset *omits,
        void *filter_data_)
 {
        struct filter_sparse_data *filter_data = filter_data_;
@@ -420,8 +441,8 @@ static enum list_objects_filter_result filter_sparse(
                if (val < 0)
                        val = frame->defval;
                if (val > 0) {
-                       if (filter_data->omits)
-                               oidset_remove(filter_data->omits, &obj->oid);
+                       if (omits)
+                               oidset_remove(omits, &obj->oid);
                        return LOFR_MARK_SEEN | LOFR_DO_SHOW;
                }
 
@@ -435,8 +456,8 @@ static enum list_objects_filter_result filter_sparse(
                 * Leave the LOFR_ bits unset so that if the blob appears
                 * again in the traversal, we will be asked again.
                 */
-               if (filter_data->omits)
-                       oidset_insert(filter_data->omits, &obj->oid);
+               if (omits)
+                       oidset_insert(omits, &obj->oid);
 
                /*
                 * Remember that at least 1 blob in this tree was
@@ -456,14 +477,11 @@ static void filter_sparse_free(void *filter_data)
        free(d);
 }
 
-static void *filter_sparse_oid__init(
-       struct oidset *omitted,
+static void filter_sparse_oid__init(
        struct list_objects_filter_options *filter_options,
-       filter_object_fn *filter_fn,
-       filter_free_fn *filter_free_fn)
+       struct filter *filter)
 {
        struct filter_sparse_data *d = xcalloc(1, sizeof(*d));
-       d->omits = omitted;
        if (add_excludes_from_blob_to_list(filter_options->sparse_oid_value,
                                           NULL, 0, &d->el) < 0)
                die("could not load filter specification");
@@ -473,16 +491,147 @@ static void *filter_sparse_oid__init(
        d->array_frame[d->nr].child_prov_omit = 0;
        d->nr++;
 
-       *filter_fn = filter_sparse;
-       *filter_free_fn = filter_sparse_free;
-       return d;
+       filter->filter_data = d;
+       filter->filter_object_fn = filter_sparse;
+       filter->free_fn = filter_sparse_free;
 }
 
-typedef void *(*filter_init_fn)(
-       struct oidset *omitted,
+/* A filter which only shows objects shown by all sub-filters. */
+struct combine_filter_data {
+       struct subfilter *sub;
+       size_t nr;
+};
+
+static enum list_objects_filter_result process_subfilter(
+       struct repository *r,
+       enum list_objects_filter_situation filter_situation,
+       struct object *obj,
+       const char *pathname,
+       const char *filename,
+       struct subfilter *sub)
+{
+       enum list_objects_filter_result result;
+
+       /*
+        * Check and update is_skipping_tree before oidset_contains so
+        * that is_skipping_tree gets unset even when the object is
+        * marked as seen.  As of this writing, no filter uses
+        * LOFR_MARK_SEEN on trees that also uses LOFR_SKIP_TREE, so the
+        * ordering is only theoretically important. Be cautious if you
+        * change the order of the below checks and more filters have
+        * been added!
+        */
+       if (sub->is_skipping_tree) {
+               if (filter_situation == LOFS_END_TREE &&
+                   oideq(&obj->oid, &sub->skip_tree))
+                       sub->is_skipping_tree = 0;
+               else
+                       return LOFR_ZERO;
+       }
+       if (oidset_contains(&sub->seen, &obj->oid))
+               return LOFR_ZERO;
+
+       result = list_objects_filter__filter_object(
+               r, filter_situation, obj, pathname, filename, sub->filter);
+
+       if (result & LOFR_MARK_SEEN)
+               oidset_insert(&sub->seen, &obj->oid);
+
+       if (result & LOFR_SKIP_TREE) {
+               sub->is_skipping_tree = 1;
+               sub->skip_tree = obj->oid;
+       }
+
+       return result;
+}
+
+static enum list_objects_filter_result filter_combine(
+       struct repository *r,
+       enum list_objects_filter_situation filter_situation,
+       struct object *obj,
+       const char *pathname,
+       const char *filename,
+       struct oidset *omits,
+       void *filter_data)
+{
+       struct combine_filter_data *d = filter_data;
+       enum list_objects_filter_result combined_result =
+               LOFR_DO_SHOW | LOFR_MARK_SEEN | LOFR_SKIP_TREE;
+       size_t sub;
+
+       for (sub = 0; sub < d->nr; sub++) {
+               enum list_objects_filter_result sub_result = process_subfilter(
+                       r, filter_situation, obj, pathname, filename,
+                       &d->sub[sub]);
+               if (!(sub_result & LOFR_DO_SHOW))
+                       combined_result &= ~LOFR_DO_SHOW;
+               if (!(sub_result & LOFR_MARK_SEEN))
+                       combined_result &= ~LOFR_MARK_SEEN;
+               if (!d->sub[sub].is_skipping_tree)
+                       combined_result &= ~LOFR_SKIP_TREE;
+       }
+
+       return combined_result;
+}
+
+static void filter_combine__free(void *filter_data)
+{
+       struct combine_filter_data *d = filter_data;
+       size_t sub;
+       for (sub = 0; sub < d->nr; sub++) {
+               list_objects_filter__free(d->sub[sub].filter);
+               oidset_clear(&d->sub[sub].seen);
+               if (d->sub[sub].omits.set.size)
+                       BUG("expected oidset to be cleared already");
+       }
+       free(d->sub);
+}
+
+static void add_all(struct oidset *dest, struct oidset *src) {
+       struct oidset_iter iter;
+       struct object_id *src_oid;
+
+       oidset_iter_init(src, &iter);
+       while ((src_oid = oidset_iter_next(&iter)) != NULL)
+               oidset_insert(dest, src_oid);
+}
+
+static void filter_combine__finalize_omits(
+       struct oidset *omits,
+       void *filter_data)
+{
+       struct combine_filter_data *d = filter_data;
+       size_t sub;
+
+       for (sub = 0; sub < d->nr; sub++) {
+               add_all(omits, &d->sub[sub].omits);
+               oidset_clear(&d->sub[sub].omits);
+       }
+}
+
+static void filter_combine__init(
        struct list_objects_filter_options *filter_options,
-       filter_object_fn *filter_fn,
-       filter_free_fn *filter_free_fn);
+       struct filter* filter)
+{
+       struct combine_filter_data *d = xcalloc(1, sizeof(*d));
+       size_t sub;
+
+       d->nr = filter_options->sub_nr;
+       d->sub = xcalloc(d->nr, sizeof(*d->sub));
+       for (sub = 0; sub < d->nr; sub++)
+               d->sub[sub].filter = list_objects_filter__init(
+                       filter->omits ? &d->sub[sub].omits : NULL,
+                       &filter_options->sub[sub]);
+
+       filter->filter_data = d;
+       filter->filter_object_fn = filter_combine;
+       filter->free_fn = filter_combine__free;
+       filter->finalize_omits_fn = filter_combine__finalize_omits;
+}
+
+typedef void (*filter_init_fn)(
+       struct list_objects_filter_options *filter_options,
+       struct filter *filter);
 
 /*
  * Must match "enum list_objects_filter_choice".
@@ -493,14 +642,14 @@ static filter_init_fn s_filters[] = {
        filter_blobs_limit__init,
        filter_trees_depth__init,
        filter_sparse_oid__init,
+       filter_combine__init,
 };
 
-void *list_objects_filter__init(
+struct filter *list_objects_filter__init(
        struct oidset *omitted,
-       struct list_objects_filter_options *filter_options,
-       filter_object_fn *filter_fn,
-       filter_free_fn *filter_free_fn)
+       struct list_objects_filter_options *filter_options)
 {
+       struct filter *filter;
        filter_init_fn init_fn;
 
        assert((sizeof(s_filters) / sizeof(s_filters[0])) == LOFC__COUNT);
@@ -510,10 +659,44 @@ void *list_objects_filter__init(
                    filter_options->choice);
 
        init_fn = s_filters[filter_options->choice];
-       if (init_fn)
-               return init_fn(omitted, filter_options,
-                              filter_fn, filter_free_fn);
-       *filter_fn = NULL;
-       *filter_free_fn = NULL;
-       return NULL;
+       if (!init_fn)
+               return NULL;
+
+       filter = xcalloc(1, sizeof(*filter));
+       filter->omits = omitted;
+       init_fn(filter_options, filter);
+       return filter;
+}
+
+enum list_objects_filter_result list_objects_filter__filter_object(
+       struct repository *r,
+       enum list_objects_filter_situation filter_situation,
+       struct object *obj,
+       const char *pathname,
+       const char *filename,
+       struct filter *filter)
+{
+       if (filter && (obj->flags & NOT_USER_GIVEN))
+               return filter->filter_object_fn(r, filter_situation, obj,
+                                               pathname, filename,
+                                               filter->omits,
+                                               filter->filter_data);
+       /*
+        * No filter is active or user gave object explicitly. In this case,
+        * always show the object (except when LOFS_END_TREE, since this tree
+        * had already been shown when LOFS_BEGIN_TREE).
+        */
+       if (filter_situation == LOFS_END_TREE)
+               return 0;
+       return LOFR_MARK_SEEN | LOFR_DO_SHOW;
+}
+
+void list_objects_filter__free(struct filter *filter)
+{
+       if (!filter)
+               return;
+       if (filter->finalize_omits_fn && filter->omits)
+               filter->finalize_omits_fn(filter->omits, filter->filter_data);
+       filter->free_fn(filter->filter_data);
+       free(filter);
 }
index 1d45a4ad5786c915f1fd597ce782cd96b7301532..cfd784e203f30fa6d85980fb019e83c37cc935e2 100644 (file)
@@ -60,30 +60,36 @@ enum list_objects_filter_situation {
        LOFS_BLOB
 };
 
-typedef enum list_objects_filter_result (*filter_object_fn)(
+struct filter;
+
+/*
+ * Constructor for the set of defined list-objects filters.
+ * The `omitted` set is optional. It is populated with objects that the
+ * filter excludes. This set should not be considered finalized until
+ * after list_objects_filter__free is called on the returned `struct
+ * filter *`.
+ */
+struct filter *list_objects_filter__init(
+       struct oidset *omitted,
+       struct list_objects_filter_options *filter_options);
+
+/*
+ * Lets `filter` decide how to handle the `obj`. If `filter` is NULL, this
+ * function behaves as expected if no filter is configured: all objects are
+ * included.
+ */
+enum list_objects_filter_result list_objects_filter__filter_object(
        struct repository *r,
        enum list_objects_filter_situation filter_situation,
        struct object *obj,
        const char *pathname,
        const char *filename,
-       void *filter_data);
-
-typedef void (*filter_free_fn)(void *filter_data);
+       struct filter *filter);
 
 /*
- * Constructor for the set of defined list-objects filters.
- * Returns a generic "void *filter_data".
- *
- * The returned "filter_fn" will be used by traverse_commit_list()
- * to filter the results.
- *
- * The returned "filter_free_fn" is a destructor for the
- * filter_data.
+ * Destroys `filter` and finalizes the `omitted` set, if present. Does
+ * nothing if `filter` is null.
  */
-void *list_objects_filter__init(
-       struct oidset *omitted,
-       struct list_objects_filter_options *filter_options,
-       filter_object_fn *filter_fn,
-       filter_free_fn *filter_free_fn);
+void list_objects_filter__free(struct filter *filter);
 
 #endif /* LIST_OBJECTS_FILTER_H */
index b5651ddd5bfdd6cda4047f71cc3cb66fc31a793d..9307d91fb3fc8bee9cbeb1a6387b5d5bf92dbd4d 100644 (file)
@@ -18,8 +18,7 @@ struct traversal_context {
        show_object_fn show_object;
        show_commit_fn show_commit;
        void *show_data;
-       filter_object_fn filter_fn;
-       void *filter_data;
+       struct filter *filter;
 };
 
 static void process_blob(struct traversal_context *ctx,
@@ -29,7 +28,7 @@ static void process_blob(struct traversal_context *ctx,
 {
        struct object *obj = &blob->object;
        size_t pathlen;
-       enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
+       enum list_objects_filter_result r;
 
        if (!ctx->revs->blob_objects)
                return;
@@ -54,11 +53,10 @@ static void process_blob(struct traversal_context *ctx,
 
        pathlen = path->len;
        strbuf_addstr(path, name);
-       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
-               r = ctx->filter_fn(ctx->revs->repo,
-                                  LOFS_BLOB, obj,
-                                  path->buf, &path->buf[pathlen],
-                                  ctx->filter_data);
+       r = list_objects_filter__filter_object(ctx->revs->repo,
+                                              LOFS_BLOB, obj,
+                                              path->buf, &path->buf[pathlen],
+                                              ctx->filter);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
@@ -157,7 +155,7 @@ static void process_tree(struct traversal_context *ctx,
        struct object *obj = &tree->object;
        struct rev_info *revs = ctx->revs;
        int baselen = base->len;
-       enum list_objects_filter_result r = LOFR_MARK_SEEN | LOFR_DO_SHOW;
+       enum list_objects_filter_result r;
        int failed_parse;
 
        if (!revs->tree_objects)
@@ -186,11 +184,10 @@ static void process_tree(struct traversal_context *ctx,
        }
 
        strbuf_addstr(base, name);
-       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn)
-               r = ctx->filter_fn(ctx->revs->repo,
-                                  LOFS_BEGIN_TREE, obj,
-                                  base->buf, &base->buf[baselen],
-                                  ctx->filter_data);
+       r = list_objects_filter__filter_object(ctx->revs->repo,
+                                              LOFS_BEGIN_TREE, obj,
+                                              base->buf, &base->buf[baselen],
+                                              ctx->filter);
        if (r & LOFR_MARK_SEEN)
                obj->flags |= SEEN;
        if (r & LOFR_DO_SHOW)
@@ -203,16 +200,14 @@ static void process_tree(struct traversal_context *ctx,
        else if (!failed_parse)
                process_tree_contents(ctx, tree, base);
 
-       if ((obj->flags & NOT_USER_GIVEN) && ctx->filter_fn) {
-               r = ctx->filter_fn(ctx->revs->repo,
-                                  LOFS_END_TREE, obj,
-                                  base->buf, &base->buf[baselen],
-                                  ctx->filter_data);
-               if (r & LOFR_MARK_SEEN)
-                       obj->flags |= SEEN;
-               if (r & LOFR_DO_SHOW)
-                       ctx->show_object(obj, base->buf, ctx->show_data);
-       }
+       r = list_objects_filter__filter_object(ctx->revs->repo,
+                                              LOFS_END_TREE, obj,
+                                              base->buf, &base->buf[baselen],
+                                              ctx->filter);
+       if (r & LOFR_MARK_SEEN)
+               obj->flags |= SEEN;
+       if (r & LOFR_DO_SHOW)
+               ctx->show_object(obj, base->buf, ctx->show_data);
 
        strbuf_setlen(base, baselen);
        free_tree_buffer(tree);
@@ -402,8 +397,7 @@ void traverse_commit_list(struct rev_info *revs,
        ctx.show_commit = show_commit;
        ctx.show_object = show_object;
        ctx.show_data = show_data;
-       ctx.filter_fn = NULL;
-       ctx.filter_data = NULL;
+       ctx.filter = NULL;
        do_traverse(&ctx);
 }
 
@@ -416,17 +410,12 @@ void traverse_commit_list_filtered(
        struct oidset *omitted)
 {
        struct traversal_context ctx;
-       filter_free_fn filter_free_fn = NULL;
 
        ctx.revs = revs;
        ctx.show_object = show_object;
        ctx.show_commit = show_commit;
        ctx.show_data = show_data;
-       ctx.filter_fn = NULL;
-
-       ctx.filter_data = list_objects_filter__init(omitted, filter_options,
-                                                   &ctx.filter_fn, &filter_free_fn);
+       ctx.filter = list_objects_filter__init(omitted, filter_options);
        do_traverse(&ctx);
-       if (ctx.filter_data && filter_free_fn)
-               filter_free_fn(ctx.filter_data);
+       list_objects_filter__free(ctx.filter);
 }
index 5b8d46aedee72aab8b027cf4310b4234268965b6..d65a8971db732782822feede96e02f86d336e547 100644 (file)
@@ -32,6 +32,20 @@ struct ll_merge_driver {
        char *cmdline;
 };
 
+static struct attr_check *merge_attributes;
+static struct attr_check *load_merge_attributes(void)
+{
+       if (!merge_attributes)
+               merge_attributes = attr_check_initl("merge", "conflict-marker-size", NULL);
+       return merge_attributes;
+}
+
+void reset_merge_attributes(void)
+{
+       attr_check_free(merge_attributes);
+       merge_attributes = NULL;
+}
+
 /*
  * Built-in low-levels
  */
@@ -354,7 +368,7 @@ int ll_merge(mmbuffer_t *result_buf,
             struct index_state *istate,
             const struct ll_merge_options *opts)
 {
-       static struct attr_check *check;
+       struct attr_check *check = load_merge_attributes();
        static const struct ll_merge_options default_opts;
        const char *ll_driver_name = NULL;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
@@ -369,9 +383,6 @@ int ll_merge(mmbuffer_t *result_buf,
                normalize_file(theirs, path, istate);
        }
 
-       if (!check)
-               check = attr_check_initl("merge", "conflict-marker-size", NULL);
-
        git_check_attr(istate, path, check);
        ll_driver_name = check->items[0].value;
        if (check->items[1].value) {
index b9e2af1c884042adda8127a07ce8565b64303047..e78973dd55ed596e26dcf4a372671ad03b97986a 100644 (file)
@@ -26,5 +26,6 @@ int ll_merge(mmbuffer_t *result_buf,
             const struct ll_merge_options *opts);
 
 int ll_merge_marker_size(struct index_state *istate, const char *path);
+void reset_merge_attributes(void);
 
 #endif
index fc43a6c52c75a32548c20bbc4a5aa7d0cc3ddd0d..1a7d69fe32a8808dd34063c48daf61e32886b47a 100644 (file)
@@ -17,6 +17,7 @@
 #include "object-store.h"
 #include "midx.h"
 #include "commit-graph.h"
+#include "promisor-remote.h"
 
 char *odb_pack_name(struct strbuf *buf,
                    const unsigned char *sha1,
@@ -287,13 +288,6 @@ static int unuse_one_window(struct packed_git *current)
        return 0;
 }
 
-void release_pack_memory(size_t need)
-{
-       size_t cur = pack_mapped;
-       while (need >= (cur - pack_mapped) && unuse_one_window(NULL))
-               ; /* nothing */
-}
-
 void close_pack_windows(struct packed_git *p)
 {
        while (p->windows) {
@@ -710,23 +704,12 @@ void unuse_pack(struct pack_window **w_cursor)
        }
 }
 
-static void try_to_free_pack_memory(size_t size)
-{
-       release_pack_memory(size);
-}
-
 struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
 {
-       static int have_set_try_to_free_routine;
        struct stat st;
        size_t alloc;
        struct packed_git *p;
 
-       if (!have_set_try_to_free_routine) {
-               have_set_try_to_free_routine = 1;
-               set_try_to_free_routine(try_to_free_pack_memory);
-       }
-
        /*
         * Make sure a corresponding .pack file exists and that
         * the index looks sane.
@@ -2150,7 +2133,7 @@ int is_promisor_object(const struct object_id *oid)
        static int promisor_objects_prepared;
 
        if (!promisor_objects_prepared) {
-               if (repository_format_partial_clone) {
+               if (has_promisor_remote()) {
                        for_each_packed_object(add_promisor_object,
                                               &promisor_objects,
                                               FOR_EACH_OBJECT_PROMISOR_ONLY);
index 3e98910bdd191f45d3dd86ff0360f40060944705..fc7904ec8147004cdcec5f013a7bace27c8697ed 100644 (file)
@@ -100,7 +100,7 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local);
  * Does not unlink if 'force_delete' is false and the pack-file is
  * marked as ".keep".
  */
-extern void unlink_pack_path(const char *pack_name, int force_delete);
+void unlink_pack_path(const char *pack_name, int force_delete);
 
 /*
  * Make sure that a pointer access into an mmap'd index file is within bounds,
index 87b26a1d922e8905fe369b164ae5cad7ea372426..b42f54d48b96c05a207ffb02eac8c235c2b723b0 100644 (file)
@@ -780,7 +780,8 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                        continue;
                }
 
-               if (!arg[2]) { /* "--" */
+               if (!arg[2] /* "--" */ ||
+                   !strcmp(arg + 2, "end-of-options")) {
                        if (!(ctx->flags & PARSE_OPT_KEEP_DASHDASH)) {
                                ctx->argc--;
                                ctx->argv++;
index a4bd40bb6acf90fdafefa0983fb523dca4e8b11c..38a33a087ec2aa783be7e2276d669247bd9f8d10 100644 (file)
@@ -46,6 +46,15 @@ enum parse_opt_option_flags {
        PARSE_OPT_COMP_ARG = 1024
 };
 
+enum parse_opt_result {
+       PARSE_OPT_COMPLETE = -3,
+       PARSE_OPT_HELP = -2,
+       PARSE_OPT_ERROR = -1,   /* must be the same as error() */
+       PARSE_OPT_DONE = 0,     /* fixed so that "return 0" works */
+       PARSE_OPT_NON_OPTION,
+       PARSE_OPT_UNKNOWN
+};
+
 struct option;
 typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
 
@@ -241,15 +250,6 @@ const char *optname(const struct option *opt, int flags);
 
 /*----- incremental advanced APIs -----*/
 
-enum parse_opt_result {
-       PARSE_OPT_COMPLETE = -3,
-       PARSE_OPT_HELP = -2,
-       PARSE_OPT_ERROR = -1,   /* must be the same as error() */
-       PARSE_OPT_DONE = 0,     /* fixed so that "return 0" works */
-       PARSE_OPT_NON_OPTION,
-       PARSE_OPT_UNKNOWN
-};
-
 /*
  * It's okay for the caller to consume argv/argc in the usual way.
  * Other fields of that structure are private to parse-options and should not
diff --git a/path.c b/path.c
index 25e97b8c3f76ce9246d8d985adba9777acd5f43c..e3da1f3c4e2c7ed077c1ed3a98103b178045a45a 100644 (file)
--- a/path.c
+++ b/path.c
@@ -1221,31 +1221,52 @@ static inline int chomp_trailing_dir_sep(const char *path, int len)
 }
 
 /*
- * If path ends with suffix (complete path components), returns the
- * part before suffix (sans trailing directory separators).
- * Otherwise returns NULL.
+ * If path ends with suffix (complete path components), returns the offset of
+ * the last character in the path before the suffix (sans trailing directory
+ * separators), and -1 otherwise.
  */
-char *strip_path_suffix(const char *path, const char *suffix)
+static ssize_t stripped_path_suffix_offset(const char *path, const char *suffix)
 {
        int path_len = strlen(path), suffix_len = strlen(suffix);
 
        while (suffix_len) {
                if (!path_len)
-                       return NULL;
+                       return -1;
 
                if (is_dir_sep(path[path_len - 1])) {
                        if (!is_dir_sep(suffix[suffix_len - 1]))
-                               return NULL;
+                               return -1;
                        path_len = chomp_trailing_dir_sep(path, path_len);
                        suffix_len = chomp_trailing_dir_sep(suffix, suffix_len);
                }
                else if (path[--path_len] != suffix[--suffix_len])
-                       return NULL;
+                       return -1;
        }
 
        if (path_len && !is_dir_sep(path[path_len - 1]))
-               return NULL;
-       return xstrndup(path, chomp_trailing_dir_sep(path, path_len));
+               return -1;
+       return chomp_trailing_dir_sep(path, path_len);
+}
+
+/*
+ * Returns true if the path ends with components, considering only complete path
+ * components, and false otherwise.
+ */
+int ends_with_path_components(const char *path, const char *components)
+{
+       return stripped_path_suffix_offset(path, components) != -1;
+}
+
+/*
+ * If path ends with suffix (complete path components), returns the
+ * part before suffix (sans trailing directory separators).
+ * Otherwise returns NULL.
+ */
+char *strip_path_suffix(const char *path, const char *suffix)
+{
+       ssize_t offset = stripped_path_suffix_offset(path, suffix);
+
+       return offset == -1 ? NULL : xstrndup(path, offset);
 }
 
 int daemon_avoid_alias(const char *p)
diff --git a/path.h b/path.h
index 2ba6ca58c83487b5e02a71ce7c2f0d556c59aebe..14d6dcad161e3720629ed60d1ec7e4a4b3e34fdd 100644 (file)
--- a/path.h
+++ b/path.h
@@ -193,4 +193,7 @@ const char *git_path_merge_head(struct repository *r);
 const char *git_path_fetch_head(struct repository *r);
 const char *git_path_shallow(struct repository *r);
 
+
+int ends_with_path_components(const char *path, const char *components);
+
 #endif /* PATH_H */
diff --git a/promisor-remote.c b/promisor-remote.c
new file mode 100644 (file)
index 0000000..9bc296c
--- /dev/null
@@ -0,0 +1,265 @@
+#include "cache.h"
+#include "object-store.h"
+#include "promisor-remote.h"
+#include "config.h"
+#include "transport.h"
+
+static char *repository_format_partial_clone;
+static const char *core_partial_clone_filter_default;
+
+void set_repository_format_partial_clone(char *partial_clone)
+{
+       repository_format_partial_clone = xstrdup_or_null(partial_clone);
+}
+
+static int fetch_refs(const char *remote_name, struct ref *ref)
+{
+       struct remote *remote;
+       struct transport *transport;
+       int original_fetch_if_missing = fetch_if_missing;
+       int res;
+
+       fetch_if_missing = 0;
+       remote = remote_get(remote_name);
+       if (!remote->url[0])
+               die(_("Remote with no URL"));
+       transport = transport_get(remote, remote->url[0]);
+
+       transport_set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
+       transport_set_option(transport, TRANS_OPT_NO_DEPENDENTS, "1");
+       res = transport_fetch_refs(transport, ref);
+       fetch_if_missing = original_fetch_if_missing;
+
+       return res;
+}
+
+static int fetch_objects(const char *remote_name,
+                        const struct object_id *oids,
+                        int oid_nr)
+{
+       struct ref *ref = NULL;
+       int i;
+
+       for (i = 0; i < oid_nr; i++) {
+               struct ref *new_ref = alloc_ref(oid_to_hex(&oids[i]));
+               oidcpy(&new_ref->old_oid, &oids[i]);
+               new_ref->exact_oid = 1;
+               new_ref->next = ref;
+               ref = new_ref;
+       }
+       return fetch_refs(remote_name, ref);
+}
+
+static struct promisor_remote *promisors;
+static struct promisor_remote **promisors_tail = &promisors;
+
+static struct promisor_remote *promisor_remote_new(const char *remote_name)
+{
+       struct promisor_remote *r;
+
+       if (*remote_name == '/') {
+               warning(_("promisor remote name cannot begin with '/': %s"),
+                       remote_name);
+               return NULL;
+       }
+
+       FLEX_ALLOC_STR(r, name, remote_name);
+
+       *promisors_tail = r;
+       promisors_tail = &r->next;
+
+       return r;
+}
+
+static struct promisor_remote *promisor_remote_lookup(const char *remote_name,
+                                                     struct promisor_remote **previous)
+{
+       struct promisor_remote *r, *p;
+
+       for (p = NULL, r = promisors; r; p = r, r = r->next)
+               if (!strcmp(r->name, remote_name)) {
+                       if (previous)
+                               *previous = p;
+                       return r;
+               }
+
+       return NULL;
+}
+
+static void promisor_remote_move_to_tail(struct promisor_remote *r,
+                                        struct promisor_remote *previous)
+{
+       if (previous)
+               previous->next = r->next;
+       else
+               promisors = r->next ? r->next : r;
+       r->next = NULL;
+       *promisors_tail = r;
+       promisors_tail = &r->next;
+}
+
+static int promisor_remote_config(const char *var, const char *value, void *data)
+{
+       const char *name;
+       int namelen;
+       const char *subkey;
+
+       if (!strcmp(var, "core.partialclonefilter"))
+               return git_config_string(&core_partial_clone_filter_default,
+                                        var, value);
+
+       if (parse_config_key(var, "remote", &name, &namelen, &subkey) < 0)
+               return 0;
+
+       if (!strcmp(subkey, "promisor")) {
+               char *remote_name;
+
+               if (!git_config_bool(var, value))
+                       return 0;
+
+               remote_name = xmemdupz(name, namelen);
+
+               if (!promisor_remote_lookup(remote_name, NULL))
+                       promisor_remote_new(remote_name);
+
+               free(remote_name);
+               return 0;
+       }
+       if (!strcmp(subkey, "partialclonefilter")) {
+               struct promisor_remote *r;
+               char *remote_name = xmemdupz(name, namelen);
+
+               r = promisor_remote_lookup(remote_name, NULL);
+               if (!r)
+                       r = promisor_remote_new(remote_name);
+
+               free(remote_name);
+
+               if (!r)
+                       return 0;
+
+               return git_config_string(&r->partial_clone_filter, var, value);
+       }
+
+       return 0;
+}
+
+static int initialized;
+
+static void promisor_remote_init(void)
+{
+       if (initialized)
+               return;
+       initialized = 1;
+
+       git_config(promisor_remote_config, NULL);
+
+       if (repository_format_partial_clone) {
+               struct promisor_remote *o, *previous;
+
+               o = promisor_remote_lookup(repository_format_partial_clone,
+                                          &previous);
+               if (o)
+                       promisor_remote_move_to_tail(o, previous);
+               else
+                       promisor_remote_new(repository_format_partial_clone);
+       }
+}
+
+static void promisor_remote_clear(void)
+{
+       while (promisors) {
+               struct promisor_remote *r = promisors;
+               promisors = promisors->next;
+               free(r);
+       }
+
+       promisors_tail = &promisors;
+}
+
+void promisor_remote_reinit(void)
+{
+       initialized = 0;
+       promisor_remote_clear();
+       promisor_remote_init();
+}
+
+struct promisor_remote *promisor_remote_find(const char *remote_name)
+{
+       promisor_remote_init();
+
+       if (!remote_name)
+               return promisors;
+
+       return promisor_remote_lookup(remote_name, NULL);
+}
+
+int has_promisor_remote(void)
+{
+       return !!promisor_remote_find(NULL);
+}
+
+static int remove_fetched_oids(struct repository *repo,
+                              struct object_id **oids,
+                              int oid_nr, int to_free)
+{
+       int i, remaining_nr = 0;
+       int *remaining = xcalloc(oid_nr, sizeof(*remaining));
+       struct object_id *old_oids = *oids;
+       struct object_id *new_oids;
+
+       for (i = 0; i < oid_nr; i++)
+               if (oid_object_info_extended(repo, &old_oids[i], NULL,
+                                            OBJECT_INFO_SKIP_FETCH_OBJECT)) {
+                       remaining[i] = 1;
+                       remaining_nr++;
+               }
+
+       if (remaining_nr) {
+               int j = 0;
+               new_oids = xcalloc(remaining_nr, sizeof(*new_oids));
+               for (i = 0; i < oid_nr; i++)
+                       if (remaining[i])
+                               oidcpy(&new_oids[j++], &old_oids[i]);
+               *oids = new_oids;
+               if (to_free)
+                       free(old_oids);
+       }
+
+       free(remaining);
+
+       return remaining_nr;
+}
+
+int promisor_remote_get_direct(struct repository *repo,
+                              const struct object_id *oids,
+                              int oid_nr)
+{
+       struct promisor_remote *r;
+       struct object_id *remaining_oids = (struct object_id *)oids;
+       int remaining_nr = oid_nr;
+       int to_free = 0;
+       int res = -1;
+
+       promisor_remote_init();
+
+       for (r = promisors; r; r = r->next) {
+               if (fetch_objects(r->name, remaining_oids, remaining_nr) < 0) {
+                       if (remaining_nr == 1)
+                               continue;
+                       remaining_nr = remove_fetched_oids(repo, &remaining_oids,
+                                                        remaining_nr, to_free);
+                       if (remaining_nr) {
+                               to_free = 1;
+                               continue;
+                       }
+               }
+               res = 0;
+               break;
+       }
+
+       if (to_free)
+               free(remaining_oids);
+
+       return res;
+}
diff --git a/promisor-remote.h b/promisor-remote.h
new file mode 100644 (file)
index 0000000..8200dfc
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef PROMISOR_REMOTE_H
+#define PROMISOR_REMOTE_H
+
+struct object_id;
+
+/*
+ * Promisor remote linked list
+ *
+ * Information in its fields come from remote.XXX config entries or
+ * from extensions.partialclone or core.partialclonefilter.
+ */
+struct promisor_remote {
+       struct promisor_remote *next;
+       const char *partial_clone_filter;
+       const char name[FLEX_ARRAY];
+};
+
+extern void promisor_remote_reinit(void);
+extern struct promisor_remote *promisor_remote_find(const char *remote_name);
+extern int has_promisor_remote(void);
+extern int promisor_remote_get_direct(struct repository *repo,
+                                     const struct object_id *oids,
+                                     int oid_nr);
+
+/*
+ * This should be used only once from setup.c to set the value we got
+ * from the extensions.partialclone config option.
+ */
+extern void set_repository_format_partial_clone(char *partial_clone);
+
+#endif /* PROMISOR_REMOTE_H */
index 52ffa8a3139f2e427fadb7d233fb4910819becc9..cff1280975b5d400844916083c070b9ee861d1cf 100644 (file)
@@ -1599,16 +1599,17 @@ struct cache_entry *refresh_cache_entry(struct index_state *istate,
 
 #define INDEX_FORMAT_DEFAULT 3
 
-static unsigned int get_index_format_default(void)
+static unsigned int get_index_format_default(struct repository *r)
 {
        char *envversion = getenv("GIT_INDEX_VERSION");
        char *endp;
-       int value;
        unsigned int version = INDEX_FORMAT_DEFAULT;
 
        if (!envversion) {
-               if (!git_config_get_int("index.version", &value))
-                       version = value;
+               prepare_repo_settings(r);
+
+               if (r->settings.index_version >= 0)
+                       version = r->settings.index_version;
                if (version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < version) {
                        warning(_("index.version set, but the value is invalid.\n"
                                  "Using version %i"), INDEX_FORMAT_DEFAULT);
@@ -1844,18 +1845,17 @@ static void check_ce_order(struct index_state *istate)
 
 static void tweak_untracked_cache(struct index_state *istate)
 {
-       switch (git_config_get_untracked_cache()) {
-       case -1: /* keep: do nothing */
-               break;
-       case 0: /* false */
+       struct repository *r = the_repository;
+
+       prepare_repo_settings(r);
+
+       if (r->settings.core_untracked_cache  == UNTRACKED_CACHE_REMOVE) {
                remove_untracked_cache(istate);
-               break;
-       case 1: /* true */
-               add_untracked_cache(istate);
-               break;
-       default: /* unknown value: do nothing */
-               break;
+               return;
        }
+
+       if (r->settings.core_untracked_cache == UNTRACKED_CACHE_WRITE)
+               add_untracked_cache(istate);
 }
 
 static void tweak_split_index(struct index_state *istate)
@@ -2765,7 +2765,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
        }
 
        if (!istate->version) {
-               istate->version = get_index_format_default();
+               istate->version = get_index_format_default(the_repository);
                if (git_env_bool("GIT_TEST_SPLIT_INDEX", 0))
                        init_split_index(istate);
        }
index f27cfc8c3e358fa27d7aec78c0b3c44c816402b3..7338cfc67158ede687ba2c410ca6de29e414f5c4 100644 (file)
@@ -1028,7 +1028,7 @@ static const char *copy_name(const char *buf)
                if (!strncmp(cp, " <", 2))
                        return xmemdupz(buf, cp - buf);
        }
-       return "";
+       return xstrdup("");
 }
 
 static const char *copy_email(const char *buf)
@@ -1036,10 +1036,10 @@ static const char *copy_email(const char *buf)
        const char *email = strchr(buf, '<');
        const char *eoemail;
        if (!email)
-               return "";
+               return xstrdup("");
        eoemail = strchr(email, '>');
        if (!eoemail)
-               return "";
+               return xstrdup("");
        return xmemdupz(email, eoemail + 1 - email);
 }
 
diff --git a/repo-settings.c b/repo-settings.c
new file mode 100644 (file)
index 0000000..3779b85
--- /dev/null
@@ -0,0 +1,64 @@
+#include "cache.h"
+#include "config.h"
+#include "repository.h"
+
+#define UPDATE_DEFAULT_BOOL(s,v) do { if (s == -1) { s = v; } } while(0)
+
+void prepare_repo_settings(struct repository *r)
+{
+       int value;
+       char *strval;
+
+       if (r->settings.initialized)
+               return;
+
+       /* Defaults */
+       memset(&r->settings, -1, sizeof(r->settings));
+
+       if (!repo_config_get_bool(r, "core.commitgraph", &value))
+               r->settings.core_commit_graph = value;
+       if (!repo_config_get_bool(r, "gc.writecommitgraph", &value))
+               r->settings.gc_write_commit_graph = value;
+       UPDATE_DEFAULT_BOOL(r->settings.core_commit_graph, 1);
+       UPDATE_DEFAULT_BOOL(r->settings.gc_write_commit_graph, 1);
+
+       if (!repo_config_get_bool(r, "index.version", &value))
+               r->settings.index_version = value;
+       if (!repo_config_get_maybe_bool(r, "core.untrackedcache", &value)) {
+               if (value == 0)
+                       r->settings.core_untracked_cache = UNTRACKED_CACHE_REMOVE;
+               else
+                       r->settings.core_untracked_cache = UNTRACKED_CACHE_WRITE;
+       } else if (!repo_config_get_string(r, "core.untrackedcache", &strval)) {
+               if (!strcasecmp(strval, "keep"))
+                       r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
+
+               free(strval);
+       }
+
+       if (!repo_config_get_string(r, "fetch.negotiationalgorithm", &strval)) {
+               if (!strcasecmp(strval, "skipping"))
+                       r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_SKIPPING;
+               else
+                       r->settings.fetch_negotiation_algorithm = FETCH_NEGOTIATION_DEFAULT;
+       }
+
+       if (!repo_config_get_bool(r, "pack.usesparse", &value))
+               r->settings.pack_use_sparse = value;
+       if (!repo_config_get_bool(r, "feature.manyfiles", &value) && value) {
+               UPDATE_DEFAULT_BOOL(r->settings.index_version, 4);
+               UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_WRITE);
+       }
+       if (!repo_config_get_bool(r, "feature.experimental", &value) && value) {
+               UPDATE_DEFAULT_BOOL(r->settings.pack_use_sparse, 1);
+               UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_SKIPPING);
+       }
+
+       /* Hack for test programs like test-dump-untracked-cache */
+       if (ignore_untracked_cache_config)
+               r->settings.core_untracked_cache = UNTRACKED_CACHE_KEEP;
+       else
+               UPDATE_DEFAULT_BOOL(r->settings.core_untracked_cache, UNTRACKED_CACHE_KEEP);
+
+       UPDATE_DEFAULT_BOOL(r->settings.fetch_negotiation_algorithm, FETCH_NEGOTIATION_DEFAULT);
+}
index 4fb6a5885f794dea9ff7a1ecf37f27bc34e4d218..4da275e73fac1a97c39109b7bf31ae01f20e0730 100644 (file)
@@ -11,6 +11,33 @@ struct pathspec;
 struct raw_object_store;
 struct submodule_cache;
 
+enum untracked_cache_setting {
+       UNTRACKED_CACHE_UNSET = -1,
+       UNTRACKED_CACHE_REMOVE = 0,
+       UNTRACKED_CACHE_KEEP = 1,
+       UNTRACKED_CACHE_WRITE = 2
+};
+
+enum fetch_negotiation_setting {
+       FETCH_NEGOTIATION_UNSET = -1,
+       FETCH_NEGOTIATION_NONE = 0,
+       FETCH_NEGOTIATION_DEFAULT = 1,
+       FETCH_NEGOTIATION_SKIPPING = 2,
+};
+
+struct repo_settings {
+       int initialized;
+
+       int core_commit_graph;
+       int gc_write_commit_graph;
+
+       int index_version;
+       enum untracked_cache_setting core_untracked_cache;
+
+       int pack_use_sparse;
+       enum fetch_negotiation_setting fetch_negotiation_algorithm;
+};
+
 struct repository {
        /* Environment */
        /*
@@ -72,6 +99,8 @@ struct repository {
         */
        char *submodule_prefix;
 
+       struct repo_settings settings;
+
        /* Subsystems */
        /*
         * Repository's config which contains key-value pairs from the usual
@@ -157,5 +186,6 @@ int repo_read_index_unmerged(struct repository *);
  */
 void repo_update_index_if_able(struct repository *, struct lock_file *);
 
+void prepare_repo_settings(struct repository *r);
 
 #endif /* REPOSITORY_H */
index 07412297f0248aae886eeb77c3a1cab13c93039c..51690e480d5b626224e919f04dbfb8d1c3426f3e 100644 (file)
@@ -2523,6 +2523,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
        int i, flags, left, seen_dashdash, got_rev_arg = 0, revarg_opt;
        struct argv_array prune_data = ARGV_ARRAY_INIT;
        const char *submodule = NULL;
+       int seen_end_of_options = 0;
 
        if (opt)
                submodule = opt->submodule;
@@ -2552,7 +2553,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                revarg_opt |= REVARG_CANNOT_BE_FILENAME;
        for (left = i = 1; i < argc; i++) {
                const char *arg = argv[i];
-               if (*arg == '-') {
+               if (!seen_end_of_options && *arg == '-') {
                        int opts;
 
                        opts = handle_revision_pseudo_opt(submodule,
@@ -2574,6 +2575,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
                                continue;
                        }
 
+                       if (!strcmp(arg, "--end-of-options")) {
+                               seen_end_of_options = 1;
+                               continue;
+                       }
+
                        opts = handle_revision_opt(revs, argc - i, argv + i,
                                                   &left, argv, opt);
                        if (opts > 0) {
index 34ebf8ed94ad7d8df6773337d31ff7d9c2c84c4a..d648aaf416510e656a372abe54d1a28d995de889 100644 (file)
@@ -3364,6 +3364,9 @@ static int do_merge(struct repository *r,
        struct commit *head_commit, *merge_commit, *i;
        struct commit_list *bases, *j, *reversed = NULL;
        struct commit_list *to_merge = NULL, **tail = &to_merge;
+       const char *strategy = !opts->xopts_nr &&
+               (!opts->strategy || !strcmp(opts->strategy, "recursive")) ?
+               NULL : opts->strategy;
        struct merge_options o;
        int merge_arg_len, oneline_offset, can_fast_forward, ret, k;
        static struct lock_file lock;
@@ -3516,7 +3519,7 @@ static int do_merge(struct repository *r,
                goto leave_merge;
        }
 
-       if (to_merge->next) {
+       if (strategy || to_merge->next) {
                /* Octopus merge */
                struct child_process cmd = CHILD_PROCESS_INIT;
 
@@ -3530,7 +3533,14 @@ static int do_merge(struct repository *r,
                cmd.git_cmd = 1;
                argv_array_push(&cmd.args, "merge");
                argv_array_push(&cmd.args, "-s");
-               argv_array_push(&cmd.args, "octopus");
+               if (!strategy)
+                       argv_array_push(&cmd.args, "octopus");
+               else {
+                       argv_array_push(&cmd.args, strategy);
+                       for (k = 0; k < opts->xopts_nr; k++)
+                               argv_array_pushf(&cmd.args,
+                                                "-X%s", opts->xopts[k]);
+               }
                argv_array_push(&cmd.args, "--no-edit");
                argv_array_push(&cmd.args, "--no-ff");
                argv_array_push(&cmd.args, "--no-log");
@@ -4554,6 +4564,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 {
        int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
        int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
+       int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
        struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
        struct strbuf label = STRBUF_INIT;
        struct commit_list *commits = NULL, **tail = &commits, *iter;
@@ -4720,7 +4731,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
 
                if (!commit)
                        strbuf_addf(out, "%s %s\n", cmd_reset,
-                                   rebase_cousins ? "onto" : "[new root]");
+                                   rebase_cousins || root_with_onto ?
+                                   "onto" : "[new root]");
                else {
                        const char *to = NULL;
 
index 6704acbb9c93a55cb7ec69d2e045d67850bc4049..574260f6215f60e8c1aedb227c80a26c34da6c94 100644 (file)
@@ -143,6 +143,12 @@ int sequencer_remove_state(struct replay_opts *opts);
  */
 #define TODO_LIST_REBASE_COUSINS (1U << 4)
 #define TODO_LIST_APPEND_TODO_HELP (1U << 5)
+/*
+ * When generating a script that rebases merges with `--root` *and* with
+ * `--onto`, we do not want to re-generate the root commits.
+ */
+#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
+
 
 int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
                          const char **argv, unsigned flags);
diff --git a/setup.c b/setup.c
index 8dcb4631f7d330a290ab0bb6810f24999f65a1b7..25a3038277cdaaca736a82191dd6b7bb82cd9ae2 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -4,6 +4,7 @@
 #include "dir.h"
 #include "string-list.h"
 #include "chdir-notify.h"
+#include "promisor-remote.h"
 
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
@@ -478,7 +479,7 @@ static int check_repository_format_gently(const char *gitdir, struct repository_
        }
 
        repository_format_precious_objects = candidate->precious_objects;
-       repository_format_partial_clone = xstrdup_or_null(candidate->partial_clone);
+       set_repository_format_partial_clone(candidate->partial_clone);
        repository_format_worktree_config = candidate->worktree_config;
        string_list_clear(&candidate->unknown_extensions, 0);
 
index 487ea35d2d398434d2a907188d8f7ede17721fe3..e85f249a5db027594d25f9882d85d1253ddbf36f 100644 (file)
@@ -30,8 +30,8 @@
 #include "mergesort.h"
 #include "quote.h"
 #include "packfile.h"
-#include "fetch-object.h"
 #include "object-store.h"
+#include "promisor-remote.h"
 
 /* The maximum size for an object header. */
 #define MAX_HEADER_LEN 32
@@ -952,12 +952,8 @@ void *xmmap_gently(void *start, size_t length,
 
        mmap_limit_check(length);
        ret = mmap(start, length, prot, flags, fd, offset);
-       if (ret == MAP_FAILED) {
-               if (!length)
-                       return NULL;
-               release_pack_memory(length);
-               ret = mmap(start, length, prot, flags, fd, offset);
-       }
+       if (ret == MAP_FAILED && !length)
+               ret = NULL;
        return ret;
 }
 
@@ -1475,16 +1471,17 @@ int oid_object_info_extended(struct repository *r, const struct object_id *oid,
                }
 
                /* Check if it is a missing object */
-               if (fetch_if_missing && repository_format_partial_clone &&
+               if (fetch_if_missing && has_promisor_remote() &&
                    !already_retried && r == the_repository &&
                    !(flags & OBJECT_INFO_SKIP_FETCH_OBJECT)) {
                        /*
-                        * TODO Investigate having fetch_object() return
-                        * TODO error/success and stopping the music here.
-                        * TODO Pass a repository struct through fetch_object,
-                        * such that arbitrary repositories work.
+                        * TODO Investigate checking promisor_remote_get_direct()
+                        * TODO return value and stopping on error here.
+                        * TODO Pass a repository struct through
+                        * promisor_remote_get_direct(), such that arbitrary
+                        * repositories work.
                         */
-                       fetch_objects(repository_format_partial_clone, real, 1);
+                       promisor_remote_get_direct(r, real, 1);
                        already_retried = 1;
                        continue;
                }
index 2989e27b717abdabd6623299d7737e9a51641990..c665e3f96d5b67db3a3079483bbd28044fbbe120 100644 (file)
@@ -403,9 +403,9 @@ static int repo_collect_ambiguous(struct repository *r,
        return collect_ambiguous(oid, data);
 }
 
-static struct repository *sort_ambiguous_repo;
-static int sort_ambiguous(const void *a, const void *b)
+static int sort_ambiguous(const void *a, const void *b, void *ctx)
 {
+       struct repository *sort_ambiguous_repo = ctx;
        int a_type = oid_object_info(sort_ambiguous_repo, a, NULL);
        int b_type = oid_object_info(sort_ambiguous_repo, b, NULL);
        int a_type_sort;
@@ -434,10 +434,7 @@ static int sort_ambiguous(const void *a, const void *b)
 
 static void sort_ambiguous_oid_array(struct repository *r, struct oid_array *a)
 {
-       /* mutex will be needed if this code is to be made thread safe */
-       sort_ambiguous_repo = r;
-       QSORT(a->oid, a->nr, sort_ambiguous);
-       sort_ambiguous_repo = NULL;
+       QSORT_S(a->oid, a->nr, sort_ambiguous, r);
 }
 
 static enum get_oid_result get_short_oid(struct repository *r,
index d30f916858883aa312bd824a53516cb099a2e922..aa48d179a9aec236069fc88501c5c26c3569d502 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -774,8 +774,10 @@ void strbuf_addstr_xml_quoted(struct strbuf *buf, const char *s)
        }
 }
 
-static int is_rfc3986_reserved(char ch)
+int is_rfc3986_reserved_or_unreserved(char ch)
 {
+       if (is_rfc3986_unreserved(ch))
+               return 1;
        switch (ch) {
                case '!': case '*': case '\'': case '(': case ')': case ';':
                case ':': case '@': case '&': case '=': case '+': case '$':
@@ -785,20 +787,19 @@ static int is_rfc3986_reserved(char ch)
        return 0;
 }
 
-static int is_rfc3986_unreserved(char ch)
+int is_rfc3986_unreserved(char ch)
 {
        return isalnum(ch) ||
                ch == '-' || ch == '_' || ch == '.' || ch == '~';
 }
 
 static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
-                                int reserved)
+                                char_predicate allow_unencoded_fn)
 {
        strbuf_grow(sb, len);
        while (len--) {
                char ch = *s++;
-               if (is_rfc3986_unreserved(ch) ||
-                   (!reserved && is_rfc3986_reserved(ch)))
+               if (allow_unencoded_fn(ch))
                        strbuf_addch(sb, ch);
                else
                        strbuf_addf(sb, "%%%02x", (unsigned char)ch);
@@ -806,9 +807,9 @@ static void strbuf_add_urlencode(struct strbuf *sb, const char *s, size_t len,
 }
 
 void strbuf_addstr_urlencode(struct strbuf *sb, const char *s,
-                            int reserved)
+                            char_predicate allow_unencoded_fn)
 {
-       strbuf_add_urlencode(sb, s, strlen(s), reserved);
+       strbuf_add_urlencode(sb, s, strlen(s), allow_unencoded_fn);
 }
 
 static void strbuf_humanise(struct strbuf *buf, off_t bytes,
index f62278a0be59be4c6cff17f0a0adcc6361e93e82..84cf96972144fae197c6350ead80880452e3d5b2 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -672,8 +672,13 @@ void strbuf_branchname(struct strbuf *sb, const char *name,
  */
 int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
+typedef int (*char_predicate)(char ch);
+
+int is_rfc3986_unreserved(char ch);
+int is_rfc3986_reserved_or_unreserved(char ch);
+
 void strbuf_addstr_urlencode(struct strbuf *sb, const char *name,
-                            int reserved);
+                            char_predicate allow_unencoded_fn);
 
 __attribute__((format (printf,1,2)))
 int printf_ln(const char *fmt, ...);
index 7ea30e50068be8f892a289c8bf705d8096a27ec3..6d87961e419e10257b3f619d42004bae0a35d7cf 100644 (file)
@@ -44,10 +44,10 @@ set_fake_editor () {
        rm -f "$1"
        echo 'rebase -i script before editing:'
        cat "$1".tmp
-       action=pick
+       action=\&
        for line in $FAKE_LINES; do
                case $line in
-               pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d)
+               pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m)
                        action="$line";;
                exec_*|x_*|break|b)
                        echo "$line" | sed 's/_/ /g' >> "$1";;
@@ -58,11 +58,12 @@ set_fake_editor () {
                bad)
                        action="badcmd";;
                fakesha)
+                       test \& != "$action" || action=pick
                        echo "$action XXXXXXX False commit" >> "$1"
                        action=pick;;
                *)
-                       sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
-                       action=pick;;
+                       sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1"
+                       action=\&;;
                esac
        done
        echo 'rebase -i script after editing:'
index e10f5f787fca8b3f7789bcf9043d7c81929f070e..c954c709ad2f50dac563cf237f7d485a2d41b7aa 100755 (executable)
@@ -390,6 +390,9 @@ test_expect_success PERL 'required process filter should filter data' '
                EOF
                test_cmp_exclude_clean expected.log debug.log &&
 
+               # Make sure that the file appears dirty, so checkout below has to
+               # run the configured filter.
+               touch test.r &&
                filter_git checkout --quiet --no-progress empty-branch &&
                cat >expected.log <<-EOF &&
                        START
index cebc77fab0b254fc2e6f63e7eb68956b2b3dec9c..705a136ed92c99cda688f5e267204e59b0e532a9 100755 (executable)
@@ -399,4 +399,11 @@ test_expect_success 'GIT_TEST_DISALLOW_ABBREVIATED_OPTIONS works' '
                test-tool parse-options --ye
 '
 
+test_expect_success '--end-of-options treats remainder as args' '
+       test-tool parse-options \
+           --expect="verbose: -1" \
+           --expect="arg 00: --verbose" \
+           --end-of-options --verbose
+'
+
 test_done
index 6415063980b49985b25d351f5c0079f44ad92533..d4b7e535ea134569ed8bf5643edf8d435a22bc1a 100755 (executable)
@@ -26,7 +26,7 @@ promise_and_delete () {
 test_expect_success 'extensions.partialclone without filter' '
        test_create_repo server &&
        git clone --filter="blob:none" "file://$(pwd)/server" client &&
-       git -C client config --unset core.partialclonefilter &&
+       git -C client config --unset remote.origin.partialclonefilter &&
        git -C client fetch origin
 '
 
@@ -166,8 +166,9 @@ test_expect_success 'fetching of missing objects' '
        # associated packfile contains the object
        ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
        test_line_count = 1 promisorlist &&
-       IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
-       git verify-pack --verbose "$IDX" | grep "$HASH"
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH" out
 '
 
 test_expect_success 'fetching of missing objects works with ref-in-want enabled' '
@@ -182,8 +183,55 @@ test_expect_success 'fetching of missing objects works with ref-in-want enabled'
        grep "git< fetch=.*ref-in-want" trace
 '
 
+test_expect_success 'fetching of missing objects from another promisor remote' '
+       git clone "file://$(pwd)/server" server2 &&
+       test_commit -C server2 bar &&
+       git -C server2 repack -a -d --write-bitmap-index &&
+       HASH2=$(git -C server2 rev-parse bar) &&
+
+       git -C repo remote add server2 "file://$(pwd)/server2" &&
+       git -C repo config remote.server2.promisor true &&
+       git -C repo cat-file -p "$HASH2" &&
+
+       git -C repo fetch server2 &&
+       rm -rf repo/.git/objects/* &&
+       git -C repo cat-file -p "$HASH2" &&
+
+       # Ensure that the .promisor file is written, and check that its
+       # associated packfile contains the object
+       ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
+       test_line_count = 1 promisorlist &&
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH2" out
+'
+
+test_expect_success 'fetching of missing objects configures a promisor remote' '
+       git clone "file://$(pwd)/server" server3 &&
+       test_commit -C server3 baz &&
+       git -C server3 repack -a -d --write-bitmap-index &&
+       HASH3=$(git -C server3 rev-parse baz) &&
+       git -C server3 config uploadpack.allowfilter 1 &&
+
+       rm repo/.git/objects/pack/pack-*.promisor &&
+
+       git -C repo remote add server3 "file://$(pwd)/server3" &&
+       git -C repo fetch --filter="blob:none" server3 $HASH3 &&
+
+       test_cmp_config -C repo true remote.server3.promisor &&
+
+       # Ensure that the .promisor file is written, and check that its
+       # associated packfile contains the object
+       ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
+       test_line_count = 1 promisorlist &&
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH3" out
+'
+
 test_expect_success 'fetching of missing blobs works' '
-       rm -rf server repo &&
+       rm -rf server server2 repo &&
+       rm -rf server server3 repo &&
        test_create_repo server &&
        test_commit -C server foo &&
        git -C server repack -a -d --write-bitmap-index &&
@@ -234,7 +282,7 @@ test_expect_success 'rev-list stops traversal at missing and promised commit' '
 
        git -C repo config core.repositoryformatversion 1 &&
        git -C repo config extensions.partialclone "arbitrary string" &&
-       GIT_TEST_COMMIT_GRAPH=0 git -C repo rev-list --exclude-promisor-objects --objects bar >out &&
+       GIT_TEST_COMMIT_GRAPH=0 git -C repo -c core.commitGraph=false rev-list --exclude-promisor-objects --objects bar >out &&
        grep $(git -C repo rev-parse bar) out &&
        ! grep $FOO out
 '
@@ -514,8 +562,9 @@ test_expect_success 'fetching of missing objects from an HTTP server' '
        # associated packfile contains the object
        ls repo/.git/objects/pack/pack-*.promisor >promisorlist &&
        test_line_count = 1 promisorlist &&
-       IDX=$(cat promisorlist | sed "s/promisor$/idx/") &&
-       git verify-pack --verbose "$IDX" | grep "$HASH"
+       IDX=$(sed "s/promisor$/idx/" promisorlist) &&
+       git verify-pack --verbose "$IDX" >out &&
+       grep "$HASH" out
 '
 
 # DO NOT add non-httpd-specific tests here, because the last part of this
index 0c37e7180d1cde57d49c12deb28239c509d9207d..3a0de0ddaa553e82b7db8594f4c45489237fb7db 100755 (executable)
@@ -91,7 +91,12 @@ test_expect_failure 'ignore .git/ with invalid config' '
 
 test_expect_success 'early config and onbranch' '
        echo "[broken" >broken &&
-       test_with_config "[includeif \"onbranch:refs/heads/master\"]path=../broken"
+       test_with_config "[includeif \"onbranch:master\"]path=../broken"
+'
+
+test_expect_success 'onbranch config outside of git repo' '
+       test_config_global includeIf.onbranch:master.path non-existent &&
+       nongit git help
 '
 
 test_done
index 42962ed7d46f6dafa09c7b276942c32da130e300..c77721b580c1535de33f3bd109c95bb52200a691 100755 (executable)
@@ -59,17 +59,38 @@ test_expect_success 'out of bounds index.version issues warning' '
        )
 '
 
-test_expect_success 'GIT_INDEX_VERSION takes precedence over config' '
+test_index_version () {
+       INDEX_VERSION_CONFIG=$1 &&
+       FEATURE_MANY_FILES=$2 &&
+       ENV_VAR_VERSION=$3
+       EXPECTED_OUTPUT_VERSION=$4 &&
        (
                rm -f .git/index &&
-               GIT_INDEX_VERSION=4 &&
-               export GIT_INDEX_VERSION &&
-               git config --add index.version 2 &&
+               rm -f .git/config &&
+               if test "$INDEX_VERSION_CONFIG" -ne 0
+               then
+                       git config --add index.version $INDEX_VERSION_CONFIG
+               fi &&
+               git config --add feature.manyFiles $FEATURE_MANY_FILES
+               if test "$ENV_VAR_VERSION" -ne 0
+               then
+                       GIT_INDEX_VERSION=$ENV_VAR_VERSION &&
+                       export GIT_INDEX_VERSION
+               else
+                       unset GIT_INDEX_VERSION
+               fi &&
                git add a 2>&1 &&
-               echo 4 >expect &&
+               echo $EXPECTED_OUTPUT_VERSION >expect &&
                test-tool index-version <.git/index >actual &&
                test_cmp expect actual
        )
+}
+
+test_expect_success 'index version config precedence' '
+       test_index_version 2 false 4 4 &&
+       test_index_version 2 true 0 2 &&
+       test_index_version 0 true 0 4 &&
+       test_index_version 0 true 2 2
 '
 
 test_done
index 209b4c7cd8c6739d0bee566c7058419021ed1a45..583e4676839c5328f67644851749db0278473e88 100755 (executable)
@@ -7,8 +7,6 @@ This test runs git ls-files with various relative path arguments.
 
 . ./test-lib.sh
 
-new_line='
-'
 sq=\'
 
 test_expect_success 'prepare' '
index 0ea4fc46949daf8d9522ff9ae52d8583ca5e9754..40251c9f8f68cbfba145976eb912ecdd3364dc65 100755 (executable)
@@ -192,10 +192,10 @@ test_expect_success 'branch --merged with --verbose' '
        EOF
        test_cmp expect actual &&
        git branch --verbose --merged topic >actual &&
-       cat >expect <<-\EOF &&
-         master c77a0a9 second on master
-       * topic  2c939f4 [ahead 1] foo
-         zzz    c77a0a9 second on master
+       cat >expect <<-EOF &&
+         master $(git rev-parse --short master) second on master
+       * topic  $(git rev-parse --short topic ) [ahead 1] foo
+         zzz    $(git rev-parse --short zzz   ) second on master
        EOF
        test_i18ncmp expect actual
 '
index ec548654ce1cae8773ad642877e525896a6b54f4..0120f769f157d580717739cbd0932e7b1d27c22a 100755 (executable)
@@ -8,17 +8,124 @@ test_description='range-diff tests'
 # harm than good.  We need some real history.
 
 test_expect_success 'setup' '
-       git fast-import < "$TEST_DIRECTORY"/t3206/history.export
+       git fast-import < "$TEST_DIRECTORY"/t3206/history.export &&
+       test_oid_cache <<-EOF
+       # topic
+       t1 sha1:4de457d
+       t2 sha1:fccce22
+       t3 sha1:147e64e
+       t4 sha1:a63e992
+       t1 sha256:b89f8b9
+       t2 sha256:5f12aad
+       t3 sha256:ea8b273
+       t4 sha256:14b7336
+
+       # unmodified
+       u1 sha1:35b9b25
+       u2 sha1:de345ab
+       u3 sha1:9af6654
+       u4 sha1:2901f77
+       u1 sha256:e3731be
+       u2 sha256:14fadf8
+       u3 sha256:736c4bc
+       u4 sha256:673e77d
+
+       # reordered
+       r1 sha1:aca177a
+       r2 sha1:14ad629
+       r3 sha1:ee58208
+       r4 sha1:307b27a
+       r1 sha256:f59d3aa
+       r2 sha256:fb261a8
+       r3 sha256:cb2649b
+       r4 sha256:958577e
+
+       # removed (deleted)
+       d1 sha1:7657159
+       d2 sha1:43d84d3
+       d3 sha1:a740396
+       d1 sha256:e312513
+       d2 sha256:eb19258
+       d3 sha256:1ccb3c1
+
+       # added
+       a1 sha1:2716022
+       a2 sha1:b62accd
+       a3 sha1:df46cfa
+       a4 sha1:3e64548
+       a5 sha1:12b4063
+       a1 sha256:d724f4d
+       a2 sha256:1de7762
+       a3 sha256:e159431
+       a4 sha256:b3e483c
+       a5 sha256:90866a7
+
+       # rebased
+       b1 sha1:cc9c443
+       b2 sha1:c5d9641
+       b3 sha1:28cc2b6
+       b4 sha1:5628ab7
+       b5 sha1:a31b12e
+       b1 sha256:a1a8717
+       b2 sha256:20a5862
+       b3 sha256:587172a
+       b4 sha256:2721c5d
+       b5 sha256:7b57864
+
+       # changed
+       c1 sha1:a4b3333
+       c2 sha1:f51d370
+       c3 sha1:0559556
+       c4 sha1:d966c5c
+       c1 sha256:f8c2b9d
+       c2 sha256:3fb6318
+       c3 sha256:168ab68
+       c4 sha256:3526539
+
+       # changed-message
+       m1 sha1:f686024
+       m2 sha1:4ab067d
+       m3 sha1:b9cb956
+       m4 sha1:8add5f1
+       m1 sha256:31e6281
+       m2 sha256:a06bf1b
+       m3 sha256:82dc654
+       m4 sha256:48470c5
+
+       # renamed
+       n1 sha1:f258d75
+       n2 sha1:017b62d
+       n3 sha1:3ce7af6
+       n4 sha1:1e6226b
+       n1 sha256:ad52114
+       n2 sha256:3b54c8f
+       n3 sha256:3b0a644
+       n4 sha256:e461653
+
+       # added and removed
+       s1 sha1:096b1ba
+       s2 sha1:d92e698
+       s3 sha1:9a1db4d
+       s4 sha1:fea3b5c
+       s1 sha256:a7f9134
+       s2 sha256:b4c2580
+       s3 sha256:1d62aa2
+       s4 sha256:48160e8
+
+       # Empty delimiter (included so lines match neatly)
+       __ sha1:-------
+       __ sha256:-------
+       EOF
 '
 
 test_expect_success 'simple A..B A..C (unmodified)' '
        git range-diff --no-color master..topic master..unmodified \
                >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  35b9b25 s/5/A/
-       2:  fccce22 = 2:  de345ab s/4/A/
-       3:  147e64e = 3:  9af6654 s/11/B/
-       4:  a63e992 = 4:  2901f77 s/12/B/
+       1:  $(test_oid t1) = 1:  $(test_oid u1) s/5/A/
+       2:  $(test_oid t2) = 2:  $(test_oid u2) s/4/A/
+       3:  $(test_oid t3) = 3:  $(test_oid u3) s/11/B/
+       4:  $(test_oid t4) = 4:  $(test_oid u4) s/12/B/
        EOF
        test_cmp expected actual
 '
@@ -38,10 +145,10 @@ test_expect_success 'simple A B C (unmodified)' '
 test_expect_success 'trivial reordering' '
        git range-diff --no-color master topic reordered >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  aca177a s/5/A/
-       3:  147e64e = 2:  14ad629 s/11/B/
-       4:  a63e992 = 3:  ee58208 s/12/B/
-       2:  fccce22 = 4:  307b27a s/4/A/
+       1:  $(test_oid t1) = 1:  $(test_oid r1) s/5/A/
+       3:  $(test_oid t3) = 2:  $(test_oid r2) s/11/B/
+       4:  $(test_oid t4) = 3:  $(test_oid r3) s/12/B/
+       2:  $(test_oid t2) = 4:  $(test_oid r4) s/4/A/
        EOF
        test_cmp expected actual
 '
@@ -49,10 +156,10 @@ test_expect_success 'trivial reordering' '
 test_expect_success 'removed a commit' '
        git range-diff --no-color master topic removed >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  7657159 s/5/A/
-       2:  fccce22 < -:  ------- s/4/A/
-       3:  147e64e = 2:  43d84d3 s/11/B/
-       4:  a63e992 = 3:  a740396 s/12/B/
+       1:  $(test_oid t1) = 1:  $(test_oid d1) s/5/A/
+       2:  $(test_oid t2) < -:  $(test_oid __) s/4/A/
+       3:  $(test_oid t3) = 2:  $(test_oid d2) s/11/B/
+       4:  $(test_oid t4) = 3:  $(test_oid d3) s/12/B/
        EOF
        test_cmp expected actual
 '
@@ -60,11 +167,11 @@ test_expect_success 'removed a commit' '
 test_expect_success 'added a commit' '
        git range-diff --no-color master topic added >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  2716022 s/5/A/
-       2:  fccce22 = 2:  b62accd s/4/A/
-       -:  ------- > 3:  df46cfa s/6/A/
-       3:  147e64e = 4:  3e64548 s/11/B/
-       4:  a63e992 = 5:  12b4063 s/12/B/
+       1:  $(test_oid t1) = 1:  $(test_oid a1) s/5/A/
+       2:  $(test_oid t2) = 2:  $(test_oid a2) s/4/A/
+       -:  $(test_oid __) > 3:  $(test_oid a3) s/6/A/
+       3:  $(test_oid t3) = 4:  $(test_oid a4) s/11/B/
+       4:  $(test_oid t4) = 5:  $(test_oid a5) s/12/B/
        EOF
        test_cmp expected actual
 '
@@ -72,10 +179,10 @@ test_expect_success 'added a commit' '
 test_expect_success 'new base, A B C' '
        git range-diff --no-color master topic rebased >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  cc9c443 s/5/A/
-       2:  fccce22 = 2:  c5d9641 s/4/A/
-       3:  147e64e = 3:  28cc2b6 s/11/B/
-       4:  a63e992 = 4:  5628ab7 s/12/B/
+       1:  $(test_oid t1) = 1:  $(test_oid b1) s/5/A/
+       2:  $(test_oid t2) = 2:  $(test_oid b2) s/4/A/
+       3:  $(test_oid t3) = 3:  $(test_oid b3) s/11/B/
+       4:  $(test_oid t4) = 4:  $(test_oid b4) s/12/B/
        EOF
        test_cmp expected actual
 '
@@ -84,11 +191,11 @@ test_expect_success 'new base, B...C' '
        # this syntax includes the commits from master!
        git range-diff --no-color topic...rebased >actual &&
        cat >expected <<-EOF &&
-       -:  ------- > 1:  a31b12e unrelated
-       1:  4de457d = 2:  cc9c443 s/5/A/
-       2:  fccce22 = 3:  c5d9641 s/4/A/
-       3:  147e64e = 4:  28cc2b6 s/11/B/
-       4:  a63e992 = 5:  5628ab7 s/12/B/
+       -:  $(test_oid __) > 1:  $(test_oid b5) unrelated
+       1:  $(test_oid t1) = 2:  $(test_oid b1) s/5/A/
+       2:  $(test_oid t2) = 3:  $(test_oid b2) s/4/A/
+       3:  $(test_oid t3) = 4:  $(test_oid b3) s/11/B/
+       4:  $(test_oid t4) = 5:  $(test_oid b4) s/12/B/
        EOF
        test_cmp expected actual
 '
@@ -96,9 +203,9 @@ test_expect_success 'new base, B...C' '
 test_expect_success 'changed commit' '
        git range-diff --no-color topic...changed >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  a4b3333 s/5/A/
-       2:  fccce22 = 2:  f51d370 s/4/A/
-       3:  147e64e ! 3:  0559556 s/11/B/
+       1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/
+       2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/
+       3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/
            @@ file: A
              9
              10
@@ -108,7 +215,7 @@ test_expect_success 'changed commit' '
              12
              13
              14
-       4:  a63e992 ! 4:  d966c5c s/12/B/
+       4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/
            @@ file
             @@ file: A
              9
@@ -125,10 +232,10 @@ test_expect_success 'changed commit' '
 test_expect_success 'changed commit with --no-patch diff option' '
        git range-diff --no-color --no-patch topic...changed >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  a4b3333 s/5/A/
-       2:  fccce22 = 2:  f51d370 s/4/A/
-       3:  147e64e ! 3:  0559556 s/11/B/
-       4:  a63e992 ! 4:  d966c5c s/12/B/
+       1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/
+       2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/
+       3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/
+       4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/
        EOF
        test_cmp expected actual
 '
@@ -136,16 +243,16 @@ test_expect_success 'changed commit with --no-patch diff option' '
 test_expect_success 'changed commit with --stat diff option' '
        git range-diff --no-color --stat topic...changed >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  a4b3333 s/5/A/
+       1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/
             a => b | 0
             1 file changed, 0 insertions(+), 0 deletions(-)
-       2:  fccce22 = 2:  f51d370 s/4/A/
+       2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/
             a => b | 0
             1 file changed, 0 insertions(+), 0 deletions(-)
-       3:  147e64e ! 3:  0559556 s/11/B/
+       3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/
             a => b | 0
             1 file changed, 0 insertions(+), 0 deletions(-)
-       4:  a63e992 ! 4:  d966c5c s/12/B/
+       4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/
             a => b | 0
             1 file changed, 0 insertions(+), 0 deletions(-)
        EOF
@@ -155,9 +262,9 @@ test_expect_success 'changed commit with --stat diff option' '
 test_expect_success 'changed commit with sm config' '
        git range-diff --no-color --submodule=log topic...changed >actual &&
        cat >expected <<-EOF &&
-       1:  4de457d = 1:  a4b3333 s/5/A/
-       2:  fccce22 = 2:  f51d370 s/4/A/
-       3:  147e64e ! 3:  0559556 s/11/B/
+       1:  $(test_oid t1) = 1:  $(test_oid c1) s/5/A/
+       2:  $(test_oid t2) = 2:  $(test_oid c2) s/4/A/
+       3:  $(test_oid t3) ! 3:  $(test_oid c3) s/11/B/
            @@ file: A
              9
              10
@@ -167,7 +274,7 @@ test_expect_success 'changed commit with sm config' '
              12
              13
              14
-       4:  a63e992 ! 4:  d966c5c s/12/B/
+       4:  $(test_oid t4) ! 4:  $(test_oid c4) s/12/B/
            @@ file
             @@ file: A
              9
@@ -184,8 +291,8 @@ test_expect_success 'changed commit with sm config' '
 test_expect_success 'renamed file' '
        git range-diff --no-color --submodule=log topic...renamed-file >actual &&
        sed s/Z/\ /g >expected <<-EOF &&
-       1:  4de457d = 1:  f258d75 s/5/A/
-       2:  fccce22 ! 2:  017b62d s/4/A/
+       1:  $(test_oid t1) = 1:  $(test_oid n1) s/5/A/
+       2:  $(test_oid t2) ! 2:  $(test_oid n2) s/4/A/
            @@ Metadata
            ZAuthor: Thomas Rast <trast@inf.ethz.ch>
            Z
@@ -198,7 +305,7 @@ test_expect_success 'renamed file' '
            Z@@
            Z 1
            Z 2
-       3:  147e64e ! 3:  3ce7af6 s/11/B/
+       3:  $(test_oid t3) ! 3:  $(test_oid n3) s/11/B/
            @@ Metadata
            Z ## Commit message ##
            Z    s/11/B/
@@ -210,7 +317,7 @@ test_expect_success 'renamed file' '
            Z 8
            Z 9
            Z 10
-       4:  a63e992 ! 4:  1e6226b s/12/B/
+       4:  $(test_oid t4) ! 4:  $(test_oid n4) s/12/B/
            @@ Metadata
            Z ## Commit message ##
            Z    s/12/B/
@@ -229,8 +336,8 @@ test_expect_success 'renamed file' '
 test_expect_success 'file added and later removed' '
        git range-diff --no-color --submodule=log topic...added-removed >actual &&
        sed s/Z/\ /g >expected <<-EOF &&
-       1:  4de457d = 1:  096b1ba s/5/A/
-       2:  fccce22 ! 2:  d92e698 s/4/A/
+       1:  $(test_oid t1) = 1:  $(test_oid s1) s/5/A/
+       2:  $(test_oid t2) ! 2:  $(test_oid s2) s/4/A/
            @@ Metadata
            ZAuthor: Thomas Rast <trast@inf.ethz.ch>
            Z
@@ -246,7 +353,7 @@ test_expect_success 'file added and later removed' '
            Z 7
            +
            + ## new-file (new) ##
-       3:  147e64e ! 3:  9a1db4d s/11/B/
+       3:  $(test_oid t3) ! 3:  $(test_oid s3) s/11/B/
            @@ Metadata
            ZAuthor: Thomas Rast <trast@inf.ethz.ch>
            Z
@@ -262,7 +369,7 @@ test_expect_success 'file added and later removed' '
            Z 14
            +
            + ## new-file (deleted) ##
-       4:  a63e992 = 4:  fea3b5c s/12/B/
+       4:  $(test_oid t4) = 4:  $(test_oid s4) s/12/B/
        EOF
        test_cmp expected actual
 '
@@ -275,8 +382,8 @@ test_expect_success 'no commits on one side' '
 test_expect_success 'changed message' '
        git range-diff --no-color topic...changed-message >actual &&
        sed s/Z/\ /g >expected <<-EOF &&
-       1:  4de457d = 1:  f686024 s/5/A/
-       2:  fccce22 ! 2:  4ab067d s/4/A/
+       1:  $(test_oid t1) = 1:  $(test_oid m1) s/5/A/
+       2:  $(test_oid t2) ! 2:  $(test_oid m2) s/4/A/
            @@ Metadata
            Z ## Commit message ##
            Z    s/4/A/
@@ -286,16 +393,16 @@ test_expect_success 'changed message' '
            Z ## file ##
            Z@@
            Z 1
-       3:  147e64e = 3:  b9cb956 s/11/B/
-       4:  a63e992 = 4:  8add5f1 s/12/B/
+       3:  $(test_oid t3) = 3:  $(test_oid m3) s/11/B/
+       4:  $(test_oid t4) = 4:  $(test_oid m4) s/12/B/
        EOF
        test_cmp expected actual
 '
 
 test_expect_success 'dual-coloring' '
-       sed -e "s|^:||" >expect <<-\EOF &&
-       :<YELLOW>1:  a4b3333 = 1:  f686024 s/5/A/<RESET>
-       :<RED>2:  f51d370 <RESET><YELLOW>!<RESET><GREEN> 2:  4ab067d<RESET><YELLOW> s/4/A/<RESET>
+       sed -e "s|^:||" >expect <<-EOF &&
+       :<YELLOW>1:  $(test_oid c1) = 1:  $(test_oid m1) s/5/A/<RESET>
+       :<RED>2:  $(test_oid c2) <RESET><YELLOW>!<RESET><GREEN> 2:  $(test_oid m2)<RESET><YELLOW> s/4/A/<RESET>
        :    <REVERSE><CYAN>@@<RESET> <RESET>Metadata<RESET>
        :      ## Commit message ##<RESET>
        :         s/4/A/<RESET>
@@ -305,7 +412,7 @@ test_expect_success 'dual-coloring' '
        :      ## file ##<RESET>
        :    <CYAN> @@<RESET>
        :      1<RESET>
-       :<RED>3:  0559556 <RESET><YELLOW>!<RESET><GREEN> 3:  b9cb956<RESET><YELLOW> s/11/B/<RESET>
+       :<RED>3:  $(test_oid c3) <RESET><YELLOW>!<RESET><GREEN> 3:  $(test_oid m3)<RESET><YELLOW> s/11/B/<RESET>
        :    <REVERSE><CYAN>@@<RESET> <RESET>file: A<RESET>
        :      9<RESET>
        :      10<RESET>
@@ -315,7 +422,7 @@ test_expect_success 'dual-coloring' '
        :      12<RESET>
        :      13<RESET>
        :      14<RESET>
-       :<RED>4:  d966c5c <RESET><YELLOW>!<RESET><GREEN> 4:  8add5f1<RESET><YELLOW> s/12/B/<RESET>
+       :<RED>4:  $(test_oid c4) <RESET><YELLOW>!<RESET><GREEN> 4:  $(test_oid m4)<RESET><YELLOW> s/12/B/<RESET>
        :    <REVERSE><CYAN>@@<RESET> <RESET>file<RESET>
        :    <CYAN> @@ file: A<RESET>
        :      9<RESET>
index 704bbc65419c3b95fb6799b890df538338684596..d3fa298c6a1b382a2c6818495a401753a7dd4375 100755 (executable)
@@ -66,8 +66,9 @@ test_expect_success 'show notes entry with %N' '
 '
 
 test_expect_success 'create reflog entry' '
+       ref=$(git rev-parse --short refs/notes/commits) &&
        cat <<-EOF >expect &&
-               a1d8fa6 refs/notes/commits@{0}: notes: Notes added by '\''git notes add'\''
+               $ref refs/notes/commits@{0}: notes: Notes added by '\''git notes add'\''
        EOF
        git reflog show refs/notes/commits >actual &&
        test_cmp expect actual
@@ -134,8 +135,9 @@ test_expect_success 'can overwrite existing note with "git notes add -f"' '
 '
 
 test_expect_success 'show notes' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 7a4ca6ee52a974a66cbaa78e33214535dff1d691
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:14:13 2005 -0700
 
@@ -152,8 +154,9 @@ test_expect_success 'show notes' '
 test_expect_success 'show multi-line notes' '
        test_commit 3rd &&
        MSG="b3${LF}c3c3c3c3${LF}d3d3d3" git notes add &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect-multiline <<-EOF &&
-               commit d07d62e5208f22eb5695e7eb47667dc8b9860290
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:15:13 2005 -0700
 
@@ -174,8 +177,9 @@ test_expect_success 'show -F notes' '
        test_commit 4th &&
        echo "xyzzy" >note5 &&
        git notes add -F note5 &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect-F <<-EOF &&
-               commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:16:13 2005 -0700
 
@@ -198,10 +202,13 @@ test_expect_success 'Re-adding -F notes without -f fails' '
 '
 
 test_expect_success 'git log --pretty=raw does not show notes' '
+       commit=$(git rev-parse HEAD) &&
+       tree=$(git rev-parse HEAD^{tree}) &&
+       parent=$(git rev-parse HEAD^) &&
        cat >expect <<-EOF &&
-               commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
-               tree 05ac65288c4c4b3b709a020ae94b2ece2f2201ae
-               parent d07d62e5208f22eb5695e7eb47667dc8b9860290
+               commit $commit
+               tree $tree
+               parent $parent
                author A U Thor <author@example.com> 1112912173 -0700
                committer C O Mitter <committer@example.com> 1112912173 -0700
 
@@ -291,8 +298,9 @@ test_expect_success 'git log --no-notes resets ref list' '
 test_expect_success 'show -m notes' '
        test_commit 5th &&
        git notes add -m spam -m "foo${LF}bar${LF}baz" &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect-m <<-EOF &&
-               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:17:13 2005 -0700
 
@@ -313,8 +321,9 @@ test_expect_success 'show -m notes' '
 
 test_expect_success 'remove note with add -f -F /dev/null' '
        git notes add -f -F /dev/null &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect-rm-F <<-EOF &&
-               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:17:13 2005 -0700
 
@@ -356,14 +365,16 @@ test_expect_success 'create note with combination of -m and -F' '
 test_expect_success 'remove note with "git notes remove"' '
        git notes remove HEAD^ &&
        git notes remove &&
+       commit=$(git rev-parse HEAD) &&
+       parent=$(git rev-parse HEAD^) &&
        cat >expect-rm-remove <<-EOF &&
-               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:17:13 2005 -0700
 
                ${indent}5th
 
-               commit 0f7aa3ec6325aeb88b910453bb3eb37c49d75c11
+               commit $parent
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:16:13 2005 -0700
 
@@ -459,9 +470,11 @@ test_expect_success 'removing with --stdin --ignore-missing' '
 '
 
 test_expect_success 'list notes with "git notes list"' '
-       cat >expect <<-EOF &&
-               c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691
-               c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290
+       commit_2=$(git rev-parse 2nd) &&
+       commit_3=$(git rev-parse 3rd) &&
+       sort -t" " -k2 >expect <<-EOF &&
+               $(git rev-parse refs/notes/commits:$commit_2) $commit_2
+               $(git rev-parse refs/notes/commits:$commit_3) $commit_3
        EOF
        git notes list >actual &&
        test_cmp expect actual
@@ -474,7 +487,7 @@ test_expect_success 'list notes with "git notes"' '
 
 test_expect_success 'list specific note with "git notes list <object>"' '
        cat >expect <<-EOF &&
-               c18dc024e14f08d18d14eea0d747ff692d66d6a3
+               $(git rev-parse refs/notes/commits:$commit_3)
        EOF
        git notes list HEAD^^ >actual &&
        test_cmp expect actual
@@ -498,10 +511,11 @@ test_expect_success 'append to existing note with "git notes append"' '
 '
 
 test_expect_success '"git notes list" does not expand to "git notes list HEAD"' '
-       cat >expect_list <<-EOF &&
-               c9c6af7f78bc47490dbf3e822cf2f3c24d4b9061 7a4ca6ee52a974a66cbaa78e33214535dff1d691
-               4b6ad22357cc8a1296720574b8d2fbc22fab0671 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
-               c18dc024e14f08d18d14eea0d747ff692d66d6a3 d07d62e5208f22eb5695e7eb47667dc8b9860290
+       commit_5=$(git rev-parse 5th) &&
+       sort -t" " -k2 >expect_list <<-EOF &&
+               $(git rev-parse refs/notes/commits:$commit_2) $commit_2
+               $(git rev-parse refs/notes/commits:$commit_3) $commit_3
+               $(git rev-parse refs/notes/commits:$commit_5) $commit_5
        EOF
        git notes list >actual &&
        test_cmp expect_list actual
@@ -531,8 +545,9 @@ test_expect_success 'appending empty string to non-existing note does not create
 test_expect_success 'create other note on a different notes ref (setup)' '
        test_commit 6th &&
        GIT_NOTES_REF="refs/notes/other" git notes add -m "other note" &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect-not-other <<-EOF &&
-               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:18:13 2005 -0700
 
@@ -569,8 +584,10 @@ test_expect_success 'Do not show note when core.notesRef is overridden' '
 '
 
 test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
+       commit=$(git rev-parse HEAD) &&
+       parent=$(git rev-parse HEAD^) &&
        cat >expect-both <<-EOF &&
-               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:18:13 2005 -0700
 
@@ -582,7 +599,7 @@ test_expect_success 'Show all notes when notes.displayRef=refs/notes/*' '
                Notes (other):
                ${indent}other note
 
-               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               commit $parent
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:17:13 2005 -0700
 
@@ -616,8 +633,9 @@ test_expect_success 'notes.displayRef can be given more than once' '
 '
 
 test_expect_success 'notes.displayRef respects order' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect-both-reversed <<-EOF &&
-               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:18:13 2005 -0700
 
@@ -642,14 +660,16 @@ test_expect_success 'GIT_NOTES_DISPLAY_REF works' '
 '
 
 test_expect_success 'GIT_NOTES_DISPLAY_REF overrides config' '
+       commit=$(git rev-parse HEAD) &&
+       parent=$(git rev-parse HEAD^) &&
        cat >expect-none <<-EOF &&
-               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:18:13 2005 -0700
 
                ${indent}6th
 
-               commit 7f9ad8836c775acb134c0a055fc55fb4cd1ba361
+               commit $parent
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:17:13 2005 -0700
 
@@ -666,8 +686,9 @@ test_expect_success '--show-notes=* adds to GIT_NOTES_DISPLAY_REF' '
 '
 
 test_expect_success '--no-standard-notes' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect-commits <<-EOF &&
-               commit 2c125331118caba0ff8238b7f4958ac6e93fe39c
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:18:13 2005 -0700
 
@@ -712,8 +733,10 @@ test_expect_success 'Allow notes on non-commits (trees, blobs, tags)' '
 '
 
 test_expect_success 'create note from other note with "git notes add -C"' '
+       test_commit 7th &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit fb01e0ca8c33b6cc0c6451dde747f97df567cb5c
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:19:13 2005 -0700
 
@@ -722,7 +745,6 @@ test_expect_success 'create note from other note with "git notes add -C"' '
                Notes:
                ${indent}order test
        EOF
-       test_commit 7th &&
        git notes add -C $(git notes list HEAD^) &&
        git log -1 >actual &&
        test_cmp expect actual &&
@@ -744,8 +766,9 @@ test_expect_success 'create note from non-blob with "git notes add -C" fails' '
 '
 
 test_expect_success 'create note from blob with "git notes add -C" reuses blob id' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 9a4c31c7f722b5d517e92c64e932dd751e1413bf
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:20:13 2005 -0700
 
@@ -762,8 +785,10 @@ test_expect_success 'create note from blob with "git notes add -C" reuses blob i
 '
 
 test_expect_success 'create note from other note with "git notes add -c"' '
+       test_commit 9th &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:21:13 2005 -0700
 
@@ -772,7 +797,6 @@ test_expect_success 'create note from other note with "git notes add -c"' '
                Notes:
                ${indent}yet another note
        EOF
-       test_commit 9th &&
        MSG="yet another note" git notes add -c $(git notes list HEAD^^) &&
        git log -1 >actual &&
        test_cmp expect actual
@@ -785,8 +809,9 @@ test_expect_success 'create note from non-existing note with "git notes add -c"
 '
 
 test_expect_success 'append to note from other note with "git notes append -C"' '
+       commit=$(git rev-parse HEAD^) &&
        cat >expect <<-EOF &&
-               commit 2e0db4bc649e174d667a1cde19e725cf897a5bd2
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:21:13 2005 -0700
 
@@ -803,8 +828,9 @@ test_expect_success 'append to note from other note with "git notes append -C"'
 '
 
 test_expect_success 'create note from other note with "git notes append -c"' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:22:13 2005 -0700
 
@@ -819,8 +845,9 @@ test_expect_success 'create note from other note with "git notes append -c"' '
 '
 
 test_expect_success 'append to note from other note with "git notes append -c"' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 7c3b87ab368f81e11b1ea87b2ab99a71ccd25406
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:22:13 2005 -0700
 
@@ -837,8 +864,10 @@ test_expect_success 'append to note from other note with "git notes append -c"'
 '
 
 test_expect_success 'copy note with "git notes copy"' '
+       test_commit 11th &&
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:23:13 2005 -0700
 
@@ -849,7 +878,6 @@ test_expect_success 'copy note with "git notes copy"' '
                ${indent}
                ${indent}yet another note
        EOF
-       test_commit 11th &&
        git notes copy HEAD^ HEAD &&
        git log -1 >actual &&
        test_cmp expect actual &&
@@ -864,8 +892,9 @@ test_expect_success 'prevent overwrite with "git notes copy"' '
 '
 
 test_expect_success 'allow overwrite with "git notes copy -f"' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit a446fff8777efdc6eb8f4b7c8a5ff699484df0d5
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:23:13 2005 -0700
 
@@ -889,8 +918,10 @@ test_expect_success 'cannot copy note from object without notes' '
 '
 
 test_expect_success 'git notes copy --stdin' '
+       commit=$(git rev-parse HEAD) &&
+       parent=$(git rev-parse HEAD^) &&
        cat >expect <<-EOF &&
-               commit e871aa61182b1d95d0a6fb75445d891722863b6b
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:25:13 2005 -0700
 
@@ -901,7 +932,7 @@ test_expect_success 'git notes copy --stdin' '
                ${indent}
                ${indent}yet another note
 
-               commit 65e263ded02ae4e8839bc151095113737579dc12
+               commit $parent
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:24:13 2005 -0700
 
@@ -922,21 +953,23 @@ test_expect_success 'git notes copy --stdin' '
 '
 
 test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
+       test_commit 14th &&
+       test_commit 15th &&
+       commit=$(git rev-parse HEAD) &&
+       parent=$(git rev-parse HEAD^) &&
        cat >expect <<-EOF &&
-               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:27:13 2005 -0700
 
                ${indent}15th
 
-               commit 07c85d77059393ed0154b8c96906547a59dfcddd
+               commit $parent
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:26:13 2005 -0700
 
                ${indent}14th
        EOF
-       test_commit 14th &&
-       test_commit 15th &&
        (echo $(git rev-parse HEAD~3) $(git rev-parse HEAD^) &&
        echo $(git rev-parse HEAD~2) $(git rev-parse HEAD)) |
        git notes copy --for-rewrite=foo &&
@@ -945,8 +978,10 @@ test_expect_success 'git notes copy --for-rewrite (unconfigured)' '
 '
 
 test_expect_success 'git notes copy --for-rewrite (enabled)' '
+       commit=$(git rev-parse HEAD) &&
+       parent=$(git rev-parse HEAD^) &&
        cat >expect <<-EOF &&
-               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:27:13 2005 -0700
 
@@ -957,7 +992,7 @@ test_expect_success 'git notes copy --for-rewrite (enabled)' '
                ${indent}
                ${indent}yet another note
 
-               commit 07c85d77059393ed0154b8c96906547a59dfcddd
+               commit $parent
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:26:13 2005 -0700
 
@@ -986,8 +1021,9 @@ test_expect_success 'git notes copy --for-rewrite (disabled)' '
 '
 
 test_expect_success 'git notes copy --for-rewrite (overwrite)' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:27:13 2005 -0700
 
@@ -1015,8 +1051,9 @@ test_expect_success 'git notes copy --for-rewrite (ignore)' '
 '
 
 test_expect_success 'git notes copy --for-rewrite (append)' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:27:13 2005 -0700
 
@@ -1037,8 +1074,9 @@ test_expect_success 'git notes copy --for-rewrite (append)' '
 '
 
 test_expect_success 'git notes copy --for-rewrite (append two to one)' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:27:13 2005 -0700
 
@@ -1075,8 +1113,9 @@ test_expect_success 'git notes copy --for-rewrite (append empty)' '
 '
 
 test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:27:13 2005 -0700
 
@@ -1095,8 +1134,9 @@ test_expect_success 'GIT_NOTES_REWRITE_MODE works' '
 '
 
 test_expect_success 'GIT_NOTES_REWRITE_REF works' '
+       commit=$(git rev-parse HEAD) &&
        cat >expect <<-EOF &&
-               commit 4acf42e847e7fffbbf89ee365c20ac7caf40de89
+               commit $commit
                Author: A U Thor <author@example.com>
                Date:   Thu Apr 7 15:27:13 2005 -0700
 
index 54460beec469c0d3fe5db57508b6b762fd540cf2..831f83d211b8c731558a717bb2b615e414b7022a 100755 (executable)
@@ -35,15 +35,10 @@ test_expect_success 'many notes created with git-notes triggers fanout' '
        git ls-tree -r --name-only refs/notes/commits |
        while read path
        do
-               case "$path" in
-               ??/??????????????????????????????????????)
-                       : true
-                       ;;
-               *)
+               echo $path | grep "^../[0-9a-f]*$" || {
                        echo "Invalid path \"$path\"" &&
-                       return 1
-                       ;;
-               esac
+                       return 1;
+               }
        done
 '
 
@@ -77,15 +72,10 @@ test_expect_success 'deleting most notes triggers fanout consolidation' '
        git ls-tree -r --name-only refs/notes/commits |
        while read path
        do
-               case "$path" in
-               ????????????????????????????????????????)
-                       : true
-                       ;;
-               *)
+               echo $path | grep -v "^../.*" || {
                        echo "Invalid path \"$path\"" &&
-                       return 1
-                       ;;
-               esac
+                       return 1;
+               }
        done
 '
 
index 61748088ebcbed700a570bc0e297f48435844340..8f4102ff9e446bce66436b5e7046c84d739c226f 100755 (executable)
@@ -11,23 +11,26 @@ test_expect_success 'setup: create a few commits with notes' '
        test_tick &&
        git commit -m 1st &&
        git notes add -m "Note #1" &&
+       first=$(git rev-parse HEAD) &&
        : > file2 &&
        git add file2 &&
        test_tick &&
        git commit -m 2nd &&
        git notes add -m "Note #2" &&
+       second=$(git rev-parse HEAD) &&
        : > file3 &&
        git add file3 &&
        test_tick &&
        git commit -m 3rd &&
-       COMMIT_FILE=.git/objects/5e/e1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
+       third=$(git rev-parse HEAD) &&
+       COMMIT_FILE=$(echo $third | sed "s!^..!.git/objects/&/!") &&
        test -f $COMMIT_FILE &&
        test-tool chmtime =+0 $COMMIT_FILE &&
        git notes add -m "Note #3"
 '
 
 cat > expect <<END_OF_LOG
-commit 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
+commit $third
 Author: A U Thor <author@example.com>
 Date:   Thu Apr 7 15:15:13 2005 -0700
 
@@ -36,7 +39,7 @@ Date:   Thu Apr 7 15:15:13 2005 -0700
 Notes:
     Note #3
 
-commit 08341ad9e94faa089d60fd3f523affb25c6da189
+commit $second
 Author: A U Thor <author@example.com>
 Date:   Thu Apr 7 15:14:13 2005 -0700
 
@@ -45,7 +48,7 @@ Date:   Thu Apr 7 15:14:13 2005 -0700
 Notes:
     Note #2
 
-commit ab5f302035f2e7aaf04265f08b42034c23256e1f
+commit $first
 Author: A U Thor <author@example.com>
 Date:   Thu Apr 7 15:13:13 2005 -0700
 
@@ -70,16 +73,16 @@ test_expect_success 'remove some commits' '
 
 test_expect_success 'verify that commits are gone' '
 
-       test_must_fail git cat-file -p 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
-       git cat-file -p 08341ad9e94faa089d60fd3f523affb25c6da189 &&
-       git cat-file -p ab5f302035f2e7aaf04265f08b42034c23256e1f
+       test_must_fail git cat-file -p $third &&
+       git cat-file -p $second &&
+       git cat-file -p $first
 '
 
 test_expect_success 'verify that notes are still present' '
 
-       git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
-       git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
-       git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+       git notes show $third &&
+       git notes show $second &&
+       git notes show $first
 '
 
 test_expect_success 'prune -n does not remove notes' '
@@ -90,13 +93,10 @@ test_expect_success 'prune -n does not remove notes' '
        test_cmp expect actual
 '
 
-cat > expect <<EOF
-5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29
-EOF
 
 test_expect_success 'prune -n lists prunable notes' '
 
-
+       echo $third >expect &&
        git notes prune -n > actual &&
        test_cmp expect actual
 '
@@ -109,9 +109,9 @@ test_expect_success 'prune notes' '
 
 test_expect_success 'verify that notes are gone' '
 
-       test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
-       git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
-       git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+       test_must_fail git notes show $third &&
+       git notes show $second &&
+       git notes show $first
 '
 
 test_expect_success 'remove some commits' '
@@ -121,21 +121,18 @@ test_expect_success 'remove some commits' '
        git gc --prune=now
 '
 
-cat > expect <<EOF
-08341ad9e94faa089d60fd3f523affb25c6da189
-EOF
-
 test_expect_success 'prune -v notes' '
 
+       echo $second >expect &&
        git notes prune -v > actual &&
        test_cmp expect actual
 '
 
 test_expect_success 'verify that notes are gone' '
 
-       test_must_fail git notes show 5ee1c35e83ea47cd3cc4f8cbee0568915fbbbd29 &&
-       test_must_fail git notes show 08341ad9e94faa089d60fd3f523affb25c6da189 &&
-       git notes show ab5f302035f2e7aaf04265f08b42034c23256e1f
+       test_must_fail git notes show $third &&
+       test_must_fail git notes show $second &&
+       git notes show $first
 '
 
 test_done
index 80b23fd3269c7828660469207728f67087c82467..23469cc78937eeec8aabc26c158680c2ca60dd7d 100755 (executable)
@@ -301,6 +301,42 @@ test_expect_success 'rebase --am and --show-current-patch' '
        )
 '
 
+test_expect_success 'rebase --am and .gitattributes' '
+       test_create_repo attributes &&
+       (
+               cd attributes &&
+               test_commit init &&
+               git config filter.test.clean "sed -e '\''s/smudged/clean/g'\''" &&
+               git config filter.test.smudge "sed -e '\''s/clean/smudged/g'\''" &&
+
+               test_commit second &&
+               git checkout -b test HEAD^ &&
+
+               echo "*.txt filter=test" >.gitattributes &&
+               git add .gitattributes &&
+               test_commit third &&
+
+               echo "This text is smudged." >a.txt &&
+               git add a.txt &&
+               test_commit fourth &&
+
+               git checkout -b removal HEAD^ &&
+               git rm .gitattributes &&
+               git add -u &&
+               test_commit fifth &&
+               git cherry-pick test &&
+
+               git checkout test &&
+               git rebase master &&
+               grep "smudged" a.txt &&
+
+               git checkout removal &&
+               git reset --hard &&
+               git rebase master &&
+               grep "clean" a.txt
+       )
+'
+
 test_expect_success 'rebase--merge.sh and --show-current-patch' '
        test_create_repo conflict-merge &&
        (
index 461dd539ffd4803c62d54e22e8921fbefa2c0786..3ea67d75b51ba079c704b8689f527c7efc5ea259 100755 (executable)
@@ -29,9 +29,6 @@ Initial setup:
 
 . "$TEST_DIRECTORY"/lib-rebase.sh
 
-# WARNING: Modifications to the initial repository can change the SHA ID used
-# in the expect2 file for the 'stop on conflicting pick' test.
-
 test_expect_success 'setup' '
        test_commit A file1 &&
        test_commit B file1 &&
@@ -155,8 +152,6 @@ test_expect_success 'rebase -x with empty command fails' '
        test_i18ncmp expected actual
 '
 
-LF='
-'
 test_expect_success 'rebase -x with newline in command fails' '
        test_when_finished "git rebase --abort ||:" &&
        test_must_fail env git rebase -x "a${LF}b" @ 2>actual &&
@@ -233,25 +228,28 @@ test_expect_success 'exchange two commits' '
        set_fake_editor &&
        FAKE_LINES="2 1" git rebase -i HEAD~2 &&
        test H = $(git cat-file commit HEAD^ | sed -ne \$p) &&
-       test G = $(git cat-file commit HEAD | sed -ne \$p)
+       test G = $(git cat-file commit HEAD | sed -ne \$p) &&
+       blob1=$(git rev-parse --short HEAD^:file1) &&
+       blob2=$(git rev-parse --short HEAD:file1) &&
+       commit=$(git rev-parse --short HEAD)
 '
 
 test_expect_success 'stop on conflicting pick' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
        diff --git a/file1 b/file1
-       index f70f10e..fd79235 100644
+       index $blob1..$blob2 100644
        --- a/file1
        +++ b/file1
        @@ -1 +1 @@
        -A
        +G
        EOF
-       cat >expect2 <<-\EOF &&
+       cat >expect2 <<-EOF &&
        <<<<<<< HEAD
        D
        =======
        G
-       >>>>>>> 5d18e54... G
+       >>>>>>> $commit... G
        EOF
        git tag new-branch1 &&
        set_fake_editor &&
@@ -1003,7 +1001,7 @@ test_expect_success 'rebase -i --root temporary sentinel commit' '
        git checkout B &&
        set_fake_editor &&
        test_must_fail env FAKE_LINES="2" git rebase -i --root &&
-       git cat-file commit HEAD | grep "^tree 4b825dc642cb" &&
+       git cat-file commit HEAD | grep "^tree $EMPTY_TREE" &&
        git rebase --abort
 '
 
@@ -1161,7 +1159,7 @@ test_expect_success 'rebase -i error on commits with \ in message' '
        test_expect_code 1 grep  "      emp" error
 '
 
-test_expect_success 'short SHA-1 setup' '
+test_expect_success SHA1 'short SHA-1 setup' '
        test_when_finished "git checkout master" &&
        git checkout --orphan collide &&
        git rm -rf . &&
@@ -1173,7 +1171,7 @@ test_expect_success 'short SHA-1 setup' '
        )
 '
 
-test_expect_success 'short SHA-1 collide' '
+test_expect_success SHA1 'short SHA-1 collide' '
        test_when_finished "reset_rebase && git checkout master" &&
        git checkout collide &&
        (
index 4eff14dae53223fb432ff5d9147543850a2c9ad5..7a2da972fd373cde3cc233472acadc0ae803bd29 100755 (executable)
@@ -120,6 +120,20 @@ test_expect_success REBASE_P 'rebase passes merge strategy options correctly' '
        git rebase --continue
 '
 
+test_expect_success 'rebase -r passes merge strategy options correctly' '
+       rm -fr .git/rebase-* &&
+       git reset --hard commit-new-file-F3-on-topic-branch &&
+       test_commit merge-theirs &&
+       git reset --hard HEAD^ &&
+       test_commit some-other-commit &&
+       test_tick &&
+       git merge --no-ff merge-theirs &&
+       FAKE_LINES="1 3 edit 4 5 7 8 9" git rebase -i -f -r -m \
+               -s recursive --strategy-option=theirs HEAD~2 &&
+       test_commit force-change-ours &&
+       git rebase --continue
+'
+
 test_expect_success '--skip after failed fixup cleans commit message' '
        test_when_finished "test_might_fail git rebase --abort" &&
        git checkout -b with-conflicting-fixup &&
index a5868ea152f94108418175a4c8af583462c09fce..50e7960702da4fa0306818f305f5d735f2292f53 100755 (executable)
@@ -76,14 +76,4 @@ test_expect_success REBASE_P \
        test_must_fail git rebase --preserve-merges --rebase-merges A
 '
 
-test_expect_success '--rebase-merges incompatible with --strategy' '
-       git checkout B^0 &&
-       test_must_fail git rebase --rebase-merges -s resolve A
-'
-
-test_expect_success '--rebase-merges incompatible with --strategy-option' '
-       git checkout B^0 &&
-       test_must_fail git rebase --rebase-merges -Xignore-space-change A
-'
-
 test_done
index d8640522a08696264b9a033fe1b5ebda44632774..bec48e6a1f99a5d5db91b392987ff5b64cf16781 100755 (executable)
@@ -11,113 +11,99 @@ commit_message() {
        git log --pretty=format:%s -1 "$1"
 }
 
+# There are a few bugs in the rebase with regards to the subtree strategy, and
+# this test script tries to document them.  First, the following commit history
+# is generated (the onelines are shown, time flows from left to right):
+#
+# master1 - master2 - master3
+#                             \
+# README ---------------------- Add subproject master - master4 - files_subtree/master5
+#
+# Where the merge moves the files master[123].t into the subdirectory
+# files_subtree/ and master4 as well as files_subtree/master5 add files to that
+# directory directly.
+#
+# Then, in subsequent test cases, `git filter-branch` is used to distill just
+# the commits that touch files_subtree/. To give it a final pre-rebase touch,
+# an empty commit is added on top. The pre-rebase commit history looks like
+# this:
+#
+# Add subproject master - master4 - files_subtree/master5 - Empty commit
+#
+# where the root commit adds three files: master1.t, master2.t and master3.t.
+#
+# This commit history is then rebased onto `master3` with the
+# `-Xsubtree=files_subtree` option in three different ways:
+#
+# 1. using `--preserve-merges`
+# 2. using `--preserve-merges` and --keep-empty
+# 3. without specifying a rebase backend
+
 test_expect_success 'setup' '
        test_commit README &&
-       mkdir files &&
-       (
-               cd files &&
-               git init &&
-               test_commit master1 &&
-               test_commit master2 &&
-               test_commit master3
-       ) &&
-       git fetch files master &&
-       git branch files-master FETCH_HEAD &&
-       git read-tree --prefix=files_subtree files-master &&
-       git checkout -- files_subtree &&
-       tree=$(git write-tree) &&
-       head=$(git rev-parse HEAD) &&
-       rev=$(git rev-parse --verify files-master^0) &&
-       commit=$(git commit-tree -p $head -p $rev -m "Add subproject master" $tree) &&
-       git update-ref HEAD $commit &&
-       (
-               cd files_subtree &&
-               test_commit master4
-       ) &&
-       test_commit files_subtree/master5
-'
 
-# FAILURE: Does not preserve master4.
-test_expect_failure REBASE_P \
-       'Rebase -Xsubtree --preserve-merges --onto commit 4' '
-       reset_rebase &&
-       git checkout -b rebase-preserve-merges-4 master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
-       git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master &&
-       verbose test "$(commit_message HEAD~)" = "files_subtree/master4"
+       git init files &&
+       test_commit -C files master1 &&
+       test_commit -C files master2 &&
+       test_commit -C files master3 &&
+
+       : perform subtree merge into files_subtree/ &&
+       git fetch files refs/heads/master:refs/heads/files-master &&
+       git merge -s ours --no-commit --allow-unrelated-histories \
+               files-master &&
+       git read-tree --prefix=files_subtree -u files-master &&
+       git commit -m "Add subproject master" &&
+
+       : add two extra commits to rebase &&
+       test_commit -C files_subtree master4 &&
+       test_commit files_subtree/master5 &&
+
+       git checkout -b to-rebase &&
+       git fast-export --no-data HEAD -- files_subtree/ |
+               sed -e "s%\([0-9a-f]\{40\} \)files_subtree/%\1%" |
+               git fast-import --force --quiet &&
+       git reset --hard &&
+       git commit -m "Empty commit" --allow-empty
 '
 
-# FAILURE: Does not preserve master5.
-test_expect_failure REBASE_P \
-       'Rebase -Xsubtree --preserve-merges --onto commit 5' '
+# FAILURE: Does not preserve master4.
+test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' '
        reset_rebase &&
-       git checkout -b rebase-preserve-merges-5 master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
+       git checkout -b rebase-preserve-merges to-rebase &&
        git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master &&
+       verbose test "$(commit_message HEAD~)" = "master4" &&
        verbose test "$(commit_message HEAD)" = "files_subtree/master5"
 '
 
 # FAILURE: Does not preserve master4.
-test_expect_failure REBASE_P \
-       'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 4' '
-       reset_rebase &&
-       git checkout -b rebase-keep-empty-4 master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
-       git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
-       verbose test "$(commit_message HEAD~2)" = "files_subtree/master4"
-'
-
-# FAILURE: Does not preserve master5.
-test_expect_failure REBASE_P \
-       'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 5' '
-       reset_rebase &&
-       git checkout -b rebase-keep-empty-5 master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
-       git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
-       verbose test "$(commit_message HEAD~)" = "files_subtree/master5"
-'
-
-# FAILURE: Does not preserve Empty.
-test_expect_failure REBASE_P \
-       'Rebase -Xsubtree --keep-empty --preserve-merges --onto empty commit' '
+test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit' '
        reset_rebase &&
-       git checkout -b rebase-keep-empty-empty master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
+       git checkout -b rebase-keep-empty to-rebase &&
        git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
+       verbose test "$(commit_message HEAD~2)" = "master4" &&
+       verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
        verbose test "$(commit_message HEAD)" = "Empty commit"
 '
 
-# FAILURE: fatal: Could not parse object
-test_expect_failure 'Rebase -Xsubtree --onto commit 4' '
+test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
        reset_rebase &&
-       git checkout -b rebase-onto-4 master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
-       git rebase -Xsubtree=files_subtree --onto files-master master &&
-       verbose test "$(commit_message HEAD~2)" = "files_subtree/master4"
+       git checkout -b rebase-onto to-rebase &&
+       test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
+       : first pick results in no changes &&
+       git rebase --continue &&
+       verbose test "$(commit_message HEAD~2)" = "master4" &&
+       verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
+       verbose test "$(commit_message HEAD)" = "Empty commit"
 '
 
-# FAILURE: fatal: Could not parse object
-test_expect_failure 'Rebase -Xsubtree --onto commit 5' '
-       reset_rebase &&
-       git checkout -b rebase-onto-5 master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
-       git rebase -Xsubtree=files_subtree --onto files-master master &&
-       verbose test "$(commit_message HEAD~)" = "files_subtree/master5"
-'
-# FAILURE: fatal: Could not parse object
-test_expect_failure 'Rebase -Xsubtree --onto empty commit' '
+test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
        reset_rebase &&
-       git checkout -b rebase-onto-empty master &&
-       git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
-       git commit -m "Empty commit" --allow-empty &&
-       git rebase -Xsubtree=files_subtree --onto files-master master &&
+       git checkout -b rebase-merges-onto to-rebase &&
+       test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
+       : first pick results in no changes &&
+       git rebase --continue &&
+       verbose test "$(commit_message HEAD~2)" = "master4" &&
+       verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
        verbose test "$(commit_message HEAD)" = "Empty commit"
 '
 
index 7b6c4847ad6b0993bc192f53dec4671edfdf7f91..cefed4e6178d00ce4639f03198e12e58ac23b894 100755 (executable)
@@ -37,20 +37,27 @@ test_expect_success 'setup' '
        test_commit A &&
        git checkout -b first &&
        test_commit B &&
+       b=$(git rev-parse --short HEAD) &&
        git checkout master &&
        test_commit C &&
+       c=$(git rev-parse --short HEAD) &&
        test_commit D &&
+       d=$(git rev-parse --short HEAD) &&
        git merge --no-commit B &&
        test_tick &&
        git commit -m E &&
        git tag -m E E &&
+       e=$(git rev-parse --short HEAD) &&
        git checkout -b second C &&
        test_commit F &&
+       f=$(git rev-parse --short HEAD) &&
        test_commit G &&
+       g=$(git rev-parse --short HEAD) &&
        git checkout master &&
        git merge --no-commit G &&
        test_tick &&
        git commit -m H &&
+       h=$(git rev-parse --short HEAD) &&
        git tag -m H H &&
        git checkout A &&
        test_commit conflicting-G G.t
@@ -93,24 +100,24 @@ test_expect_success 'create completely different structure' '
 '
 
 test_expect_success 'generate correct todo list' '
-       cat >expect <<-\EOF &&
+       cat >expect <<-EOF &&
        label onto
 
        reset onto
-       pick d9df450 B
+       pick $b B
        label E
 
        reset onto
-       pick 5dee784 C
+       pick $c C
        label branch-point
-       pick ca2c861 F
-       pick 088b00a G
+       pick $f F
+       pick $g G
        label H
 
        reset branch-point # C
-       pick 12bd07b D
-       merge -C 2051b56 E # E
-       merge -C 233d48a H # H
+       pick $d D
+       merge -C $e E # E
+       merge -C $h H # H
 
        EOF
 
@@ -441,4 +448,25 @@ test_expect_success '--continue after resolving conflicts after a merge' '
        test_path_is_missing .git/MERGE_HEAD
 '
 
+test_expect_success '--rebase-merges with strategies' '
+       git checkout -b with-a-strategy F &&
+       test_tick &&
+       git merge -m "Merge conflicting-G" conflicting-G &&
+
+       : first, test with a merge strategy option &&
+       git rebase -ir -Xtheirs G &&
+       echo conflicting-G >expect &&
+       test_cmp expect G.t &&
+
+       : now, try with a merge strategy other than recursive &&
+       git reset --hard @{1} &&
+       write_script git-merge-override <<-\EOF &&
+       echo overridden$1 >>G.t
+       git add G.t
+       EOF
+       PATH="$PWD:$PATH" git rebase -ir -s override -Xxopt G &&
+       test_write_lines G overridden--xopt >expect &&
+       test_cmp expect G.t
+'
+
 test_done
index 127dd0082ff8f535eeb8dee93095cf1ce13f3a95..9d5adbc130b19bc955eb25742d89333ccc9fe22b 100755 (executable)
@@ -16,7 +16,11 @@ test_expect_success setup '
        git add file1 &&
        test_tick &&
        git commit -m "second" &&
-       git tag second
+       git tag second &&
+       test_oid_cache <<-EOF
+       cp_ff sha1:1df192cd8bc58a2b275d842cede4d221ad9000d1
+       cp_ff sha256:e70d6b7fc064bddb516b8d512c9057094b96ce6ff08e12080acc4fe7f1d60a1d
+       EOF
 '
 
 test_expect_success 'cherry-pick using --ff fast forwards' '
@@ -102,7 +106,7 @@ test_expect_success 'cherry pick a root commit with --ff' '
        git add file2 &&
        git commit --amend -m "file2" &&
        git cherry-pick --ff first &&
-       test "$(git rev-parse --verify HEAD)" = "1df192cd8bc58a2b275d842cede4d221ad9000d1"
+       test "$(git rev-parse --verify HEAD)" = "$(test_oid cp_ff)"
 '
 
 test_expect_success 'cherry-pick --ff on unborn branch' '
index 66282a720ee853e7442334c889e199978aac9260..8c8cca5bfb1eaff0474e4bc8bca423ba503a633c 100755 (executable)
@@ -240,12 +240,14 @@ test_expect_success 'refresh index before checking if it is up-to-date' '
 '
 
 test_expect_success 'choking "git rm" should not let it die with cruft' '
+       test_oid_init &&
        git reset -q --hard &&
        test_when_finished "rm -f .git/index.lock && git reset -q --hard" &&
        i=0 &&
+       hash=$(test_oid deadbeef) &&
        while test $i -lt 12000
        do
-               echo "100644 1234567890123456789012345678901234567890 0 some-file-$i"
+               echo "100644 $hash 0    some-file-$i"
                i=$(( $i + 1 ))
        done | git update-index --index-info &&
        git rm -n "some-file-*" | : &&
index 8eb47942e2d7f9624058b3347f011db591a13434..64dcc5ec289ee0b0d042f1ecefd237a1dddad2be 100755 (executable)
@@ -23,6 +23,7 @@ check_verify_failure () {
 # first create a commit, so we have a valid object/type
 # for the tag.
 test_expect_success 'setup' '
+       test_oid_init &&
        echo Hello >A &&
        git update-index --add A &&
        git commit -m "Initial commit" &&
@@ -69,28 +70,28 @@ check_verify_failure '"object" line SHA1 check' '^error: char7: .*SHA1 hash$'
 #  4. type line label check
 
 cat >tag.sig <<EOF
-object 779e9b33986b1c2670fff52c5067603117b3e895
+object $head
 xxxx tag
 tag mytag
 tagger . <> 0 +0000
 
 EOF
 
-check_verify_failure '"type" line label check' '^error: char47: .*"\\ntype "$'
+check_verify_failure '"type" line label check' '^error: char.*: .*"\\ntype "$'
 
 ############################################################
 #  5. type line eol check
 
-echo "object 779e9b33986b1c2670fff52c5067603117b3e895" >tag.sig
+echo "object $head" >tag.sig
 printf "type tagsssssssssssssssssssssssssssssss" >>tag.sig
 
-check_verify_failure '"type" line eol check' '^error: char48: .*"\\n"$'
+check_verify_failure '"type" line eol check' '^error: char.*: .*"\\n"$'
 
 ############################################################
 #  6. tag line label check #1
 
 cat >tag.sig <<EOF
-object 779e9b33986b1c2670fff52c5067603117b3e895
+object $head
 type tag
 xxx mytag
 tagger . <> 0 +0000
@@ -98,37 +99,37 @@ tagger . <> 0 +0000
 EOF
 
 check_verify_failure '"tag" line label check #1' \
-       '^error: char57: no "tag " found$'
+       '^error: char.*: no "tag " found$'
 
 ############################################################
 #  7. tag line label check #2
 
 cat >tag.sig <<EOF
-object 779e9b33986b1c2670fff52c5067603117b3e895
+object $head
 type taggggggggggggggggggggggggggggggg
 tag
 EOF
 
 check_verify_failure '"tag" line label check #2' \
-       '^error: char87: no "tag " found$'
+       '^error: char.*: no "tag " found$'
 
 ############################################################
 #  8. type line type-name length check
 
 cat >tag.sig <<EOF
-object 779e9b33986b1c2670fff52c5067603117b3e895
+object $head
 type taggggggggggggggggggggggggggggggg
 tag mytag
 EOF
 
 check_verify_failure '"type" line type-name length check' \
-       '^error: char53: type too long$'
+       '^error: char.*: type too long$'
 
 ############################################################
 #  9. verify object (SHA1/type) check
 
 cat >tag.sig <<EOF
-object 779e9b33986b1c2670fff52c5067603117b3e895
+object $(test_oid deadbeef)
 type tagggg
 tag mytag
 tagger . <> 0 +0000
@@ -150,7 +151,7 @@ tagger . <> 0 +0000
 EOF
 
 check_verify_failure 'verify tag-name check' \
-       '^error: char67: could not verify tag name$'
+       '^error: char.*: could not verify tag name$'
 
 ############################################################
 # 11. tagger line label check #1
@@ -164,7 +165,7 @@ This is filler
 EOF
 
 check_verify_failure '"tagger" line label check #1' \
-       '^error: char70: could not find "tagger "$'
+       '^error: char.*: could not find "tagger "$'
 
 ############################################################
 # 12. tagger line label check #2
@@ -179,7 +180,7 @@ This is filler
 EOF
 
 check_verify_failure '"tagger" line label check #2' \
-       '^error: char70: could not find "tagger "$'
+       '^error: char.*: could not find "tagger "$'
 
 ############################################################
 # 13. disallow missing tag author name
@@ -194,7 +195,7 @@ This is filler
 EOF
 
 check_verify_failure 'disallow missing tag author name' \
-       '^error: char77: missing tagger name$'
+       '^error: char.*: missing tagger name$'
 
 ############################################################
 # 14. disallow missing tag author name
@@ -209,7 +210,7 @@ tagger T A Gger <
 EOF
 
 check_verify_failure 'disallow malformed tagger' \
-       '^error: char77: malformed tagger field$'
+       '^error: char.*: malformed tagger field$'
 
 ############################################################
 # 15. allow empty tag email
@@ -238,7 +239,7 @@ tagger T A Gger <tag ger@example.com> 0 +0000
 EOF
 
 check_verify_failure 'disallow spaces in tag email' \
-       '^error: char77: malformed tagger field$'
+       '^error: char.*: malformed tagger field$'
 
 ############################################################
 # 17. disallow missing tag timestamp
@@ -252,7 +253,7 @@ tagger T A Gger <tagger@example.com>__
 EOF
 
 check_verify_failure 'disallow missing tag timestamp' \
-       '^error: char107: missing tag timestamp$'
+       '^error: char.*: missing tag timestamp$'
 
 ############################################################
 # 18. detect invalid tag timestamp1
@@ -266,7 +267,7 @@ tagger T A Gger <tagger@example.com> Tue Mar 25 15:47:44 2008
 EOF
 
 check_verify_failure 'detect invalid tag timestamp1' \
-       '^error: char107: missing tag timestamp$'
+       '^error: char.*: missing tag timestamp$'
 
 ############################################################
 # 19. detect invalid tag timestamp2
@@ -280,7 +281,7 @@ tagger T A Gger <tagger@example.com> 2008-03-31T12:20:15-0500
 EOF
 
 check_verify_failure 'detect invalid tag timestamp2' \
-       '^error: char111: malformed tag timestamp$'
+       '^error: char.*: malformed tag timestamp$'
 
 ############################################################
 # 20. detect invalid tag timezone1
@@ -294,7 +295,7 @@ tagger T A Gger <tagger@example.com> 1206478233 GMT
 EOF
 
 check_verify_failure 'detect invalid tag timezone1' \
-       '^error: char118: malformed tag timezone$'
+       '^error: char.*: malformed tag timezone$'
 
 ############################################################
 # 21. detect invalid tag timezone2
@@ -308,7 +309,7 @@ tagger T A Gger <tagger@example.com> 1206478233 +  30
 EOF
 
 check_verify_failure 'detect invalid tag timezone2' \
-       '^error: char118: malformed tag timezone$'
+       '^error: char.*: malformed tag timezone$'
 
 ############################################################
 # 22. detect invalid tag timezone3
@@ -322,7 +323,7 @@ tagger T A Gger <tagger@example.com> 1206478233 -1430
 EOF
 
 check_verify_failure 'detect invalid tag timezone3' \
-       '^error: char118: malformed tag timezone$'
+       '^error: char.*: malformed tag timezone$'
 
 ############################################################
 # 23. detect invalid header entry
@@ -337,7 +338,7 @@ this line should not be here
 EOF
 
 check_verify_failure 'detect invalid header entry' \
-       '^error: char124: trailing garbage in tag header$'
+       '^error: char.*: trailing garbage in tag header$'
 
 ############################################################
 # 24. create valid tag
index b8e337893f3e1e505da94b0c3f0b743ece622f10..820b350aeb6a6a9162e1ba03543a1260e6d34b3c 100755 (executable)
@@ -7,6 +7,18 @@ test_description='Test git stash'
 
 . ./test-lib.sh
 
+diff_cmp () {
+       for i in "$1" "$2"
+       do
+               sed -e 's/^index 0000000\.\.[0-9a-f]*/index 0000000..1234567/' \
+               -e 's/^index [0-9a-f]*\.\.[0-9a-f]*/index 1234567..89abcde/' \
+               -e 's/^index [0-9a-f]*,[0-9a-f]*\.\.[0-9a-f]*/index 1234567,7654321..89abcde/' \
+               "$i" >"$i.compare" || return 1
+       done &&
+       test_cmp "$1.compare" "$2.compare" &&
+       rm -f "$1.compare" "$2.compare"
+}
+
 test_expect_success 'stash some dirty working directory' '
        echo 1 >file &&
        git add file &&
@@ -36,7 +48,7 @@ EOF
 test_expect_success 'parents of stash' '
        test $(git rev-parse stash^) = $(git rev-parse HEAD) &&
        git diff stash^2..stash >output &&
-       test_cmp expect output
+       diff_cmp expect output
 '
 
 test_expect_success 'applying bogus stash does nothing' '
@@ -210,13 +222,13 @@ test_expect_success 'stash branch' '
        test refs/heads/stashbranch = $(git symbolic-ref HEAD) &&
        test $(git rev-parse HEAD) = $(git rev-parse master^) &&
        git diff --cached >output &&
-       test_cmp expect output &&
+       diff_cmp expect output &&
        git diff >output &&
-       test_cmp expect1 output &&
+       diff_cmp expect1 output &&
        git add file &&
        git commit -m alternate\ second &&
        git diff master..stashbranch >output &&
-       test_cmp output expect2 &&
+       diff_cmp output expect2 &&
        test 0 = $(git stash list | wc -l)
 '
 
@@ -577,7 +589,7 @@ test_expect_success 'stash show -p - stashes on stack, stash-like argument' '
        +bar
        EOF
        git stash show -p ${STASH_ID} >actual &&
-       test_cmp expected actual
+       diff_cmp expected actual
 '
 
 test_expect_success 'stash show - no stashes on stack, stash-like argument' '
@@ -609,7 +621,7 @@ test_expect_success 'stash show -p - no stashes on stack, stash-like argument' '
        +foo
        EOF
        git stash show -p ${STASH_ID} >actual &&
-       test_cmp expected actual
+       diff_cmp expected actual
 '
 
 test_expect_success 'stash show --patience shows diff' '
@@ -627,7 +639,7 @@ test_expect_success 'stash show --patience shows diff' '
        +foo
        EOF
        git stash show --patience ${STASH_ID} >actual &&
-       test_cmp expected actual
+       diff_cmp expected actual
 '
 
 test_expect_success 'drop: fail early if specified stash is not a stash ref' '
@@ -791,7 +803,7 @@ test_expect_success 'stash where working directory contains "HEAD" file' '
        git diff-index --cached --quiet HEAD &&
        test "$(git rev-parse stash^)" = "$(git rev-parse HEAD)" &&
        git diff stash^..stash >output &&
-       test_cmp expect output
+       diff_cmp expect output
 '
 
 test_expect_success 'store called with invalid commit' '
@@ -847,7 +859,7 @@ test_expect_success 'stash list implies --first-parent -m' '
        +working
        EOF
        git stash list --format=%gd -p >actual &&
-       test_cmp expect actual
+       diff_cmp expect actual
 '
 
 test_expect_success 'stash list --cc shows combined diff' '
@@ -864,7 +876,7 @@ test_expect_success 'stash list --cc shows combined diff' '
        ++working
        EOF
        git stash list --format=%gd -p --cc >actual &&
-       test_cmp expect actual
+       diff_cmp expect actual
 '
 
 test_expect_success 'stash is not confused by partial renames' '
index 8de36b7d12227a38769f658983363a5ebd52fd68..e5116a76a1cc660370e1f5e958f87c2844a95f83 100755 (executable)
@@ -78,7 +78,7 @@ test_expect_success 'git diff-files --no-patch --patch shows the patch' '
 
 test_expect_success 'git diff-files --no-patch --patch-with-raw shows the patch and raw data' '
        git diff-files --no-patch --patch-with-raw >actual &&
-       grep -q "^:100644 100755 .* 0000000000000000000000000000000000000000 M  path0\$" actual &&
+       grep -q "^:100644 100755 .* $ZERO_OID M path0\$" actual &&
        tail -n +4 actual >actual-patch &&
        compare_diff_patch expected actual-patch
 '
index 3a6c21e8251b84b2979ac7fc5d730a3155b624b4..cbcdd104647b242c552d39d4f0529298936f8450 100755 (executable)
@@ -7,123 +7,272 @@ test_description='Test diff raw-output.
 
 '
 . ./test-lib.sh
+
 . "$TEST_DIRECTORY"/lib-read-tree-m-3way.sh
 
-cat >.test-plain-OA <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A     AA
-:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A     AN
-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D     DD
-:000000 040000 0000000000000000000000000000000000000000 6d50f65d3bdab91c63444294d38f08aeff328e42 A     DF
-:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D     DM
-:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D     DN
-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A     LL
-:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M     MD
-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M     MM
-:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M     MN
-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M     SS
-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M     TT
-:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 5e5f22072bb39f6e12cf663a57cb634c76eefb49 M     Z
+test_oid_init
+
+test_oid_cache <<\EOF
+aa_1 sha1:ccba72ad3888a3520b39efcf780b9ee64167535d
+aa_1 sha256:9febfbf18197819b2735c45291f138525d2476d59470f98239647544586ba403
+
+aa_2 sha1:6aa2b5335b16431a0ef71e5c0a28be69183cf6a2
+aa_2 sha256:6eaa3437de83f145a4aaa6ba355303075ade547b128ec6a2cd00a81ff7ce7a56
+
+an_1 sha1:7e426fb079479fd67f6d81f984e4ec649a44bc25
+an_1 sha256:8f92a0bec99e399a38e3bd0e1bf19fbf121e0160efb29b857df79d439f1c4536
+
+dd_1 sha1:bcc68ef997017466d5c9094bcf7692295f588c9a
+dd_1 sha256:07e17428b00639b85485d2b01083d219e2f3e3ba8579e9ca44e9cc8dd554d952
+
+df_1 sha1:6d50f65d3bdab91c63444294d38f08aeff328e42
+df_1 sha256:e367cecc27e9bf5451b1c65828cb21938d36a5f8e39c1b03ad6509cc36bb8e9d
+
+df_2 sha1:71420ab81e254145d26d6fc0cddee64c1acd4787
+df_2 sha256:0f0a86d10347ff6921d03a3c954679f3f1d14fa3d5cd82f57b32c09755f3a47d
+
+dfd1 sha1:68a6d8b91da11045cf4aa3a5ab9f2a781c701249
+dfd1 sha256:f3bd3265b02b6978ce86490d8ad026c573639c974b3de1d9faf30d8d5a77d3d5
+
+dm_1 sha1:141c1f1642328e4bc46a7d801a71da392e66791e
+dm_1 sha256:c89f8656e7b94e21ee5fbaf0e2149bbf783c51edbe2ce110349cac13059ee7ed
+
+dm_2 sha1:3c4d8de5fbad08572bab8e10eef8dbb264cf0231
+dm_2 sha256:83a572e37e0c94086294dae2cecc43d9131afd6f6c906e495c78972230b54988
+
+dn_1 sha1:35abde1506ddf806572ff4d407bd06885d0f8ee9
+dn_1 sha256:775d5852582070e620be63327bfa515fab8f71c7ac3e4f0c3cd6267b4377ba28
+
+ll_2 sha1:1d41122ebdd7a640f29d3c9cc4f9d70094374762
+ll_2 sha256:7917b4948a883cfed0a77d3d5a625dc8577d6ddcc3c6c3bbc56c4d4226a2246d
+
+md_1 sha1:03f24c8c4700babccfd28b654e7e8eac402ad6cd
+md_1 sha256:fc9f30369b978595ad685ba11ca9a17de0af16d79cd4b629975f4f1590033902
+
+md_2 sha1:103d9f89b50b9aad03054b579be5e7aa665f2d57
+md_2 sha256:fc78ec75275628762fe520479a6b2398dec295ce7aabcb1d15e5963c7b4e9317
+
+mm_1 sha1:b258508afb7ceb449981bd9d63d2d3e971bf8d34
+mm_1 sha256:a4b7847d228e900e3000285e240c20fd96f9dd41ce1445305f6eada126d4a04a
+
+mm_2 sha1:b431b272d829ff3aa4d1a5085f4394ab4d3305b6
+mm_2 sha256:3f8b83ea36aacf689bcf1a1290a9a8ed341564d32682ea6f76fea9a979186782
+
+mm_3 sha1:19989d4559aae417fedee240ccf2ba315ea4dc2b
+mm_3 sha256:71b3bfc5747ac033fff9ea0ab39ee453a3af2969890e75d6ef547b87544e2681
+
+mn_1 sha1:bd084b0c27c7b6cc34f11d6d0509a29be3caf970
+mn_1 sha256:47a67450583d7a329eb01a7c4ba644945af72c0ed2c7c95eb5a00d6e46d4d483
+
+mn_2 sha1:a716d58de4a570e0038f5c307bd8db34daea021f
+mn_2 sha256:f95104c1ebe27acb84bac25a7be98c71f6b8d3054b21f357a5be0c524ad97e08
+
+nm_1 sha1:c8f25781e8f1792e3e40b74225e20553041b5226
+nm_1 sha256:09baddc7afaa62e62e152c23c9c3ab94bf15a3894031e227e9be7fe68e1f4e49
+
+nm_2 sha1:cdb9a8c3da571502ac30225e9c17beccb8387983
+nm_2 sha256:58b5227956ac2d2a08d0efa513c0ae37430948b16791ea3869a1308dbf05536d
+
+na_1 sha1:15885881ea69115351c09b38371f0348a3fb8c67
+na_1 sha256:18e4fdd1670cd7968ee23d35bfd29e5418d56fb190c840094c1c57ceee0aad8f
+
+nd_1 sha1:a4e179e4291e5536a5e1c82e091052772d2c5a93
+nd_1 sha256:07dac9b01d00956ea0c65bd993d7de4864aeef2ed3cbb1255d9f1d949fcd6df6
+
+ss_1 sha1:40c959f984c8b89a2b02520d17f00d717f024397
+ss_1 sha256:50fc1b5df74d9910db2f9270993484235f15b69b75b01bcfb53e059289d14af9
+
+ss_2 sha1:2ac547ae9614a00d1b28275de608131f7a0e259f
+ss_2 sha256:a90f02e6044f1497d13db587d22ab12f90150a7d1e084afcf96065fab35ae2bc
+
+tt_1 sha1:4ac13458899ab908ef3b1128fa378daefc88d356
+tt_1 sha256:c53113c7dd5060e86b5b251428bd058f6726f66273c6a24bff1c61a04f498dd3
+
+tt_2 sha1:4c86f9a85fbc5e6804ee2e17a797538fbe785bca
+tt_2 sha256:0775f2a296129a7cf2862b46bc0e88c14d593f2773a3e3fb1c5193db6f5a7e77
+
+tt_3 sha1:c4e4a12231b9fa79a0053cb6077fcb21bb5b135a
+tt_3 sha256:47860f93cdd211f96443e0560f21c57ab6c2f4b0ac27ff03651a352e53fe8484
+
+z__1 sha1:7d670fdcdb9929f6c7dac196ff78689cd1c566a1
+z__1 sha256:44d0f37aff5e51cfcfdd1134c93a6419bcca7b9964f792ffcd5f9b4fcba1ee63
+
+z__2 sha1:5e5f22072bb39f6e12cf663a57cb634c76eefb49
+z__2 sha256:d29de162113190fed104eb5f010820cef4e315f89b9326e8497f7219fb737894
+
+z__3 sha1:1ba523955d5160681af65cb776411f574c1e8155
+z__3 sha256:07422d772b07794ab4369a5648e617719f89c2d2212cbeab05d97214b6471636
+
+zaa1 sha1:8acb8e9750e3f644bf323fcf3d338849db106c77
+zaa1 sha256:e79b029282c8abec2d9f3f7faceaf2a1405e02d1f368e66450ae66cf5b68d1f4
+
+zaa2 sha1:6c0b99286d0bce551ac4a7b3dff8b706edff3715
+zaa2 sha256:c82bd78c3e69ea1796e6b1a7a3ba45bb106c50e819296475b862123d3f5cc5a0
+
+zan1 sha1:087494262084cefee7ed484d20c8dc0580791272
+zan1 sha256:4b159eb3804d05599023dd074f771d06d02870f4ab24a7165add8ac3d703b8d3
+
+zdd1 sha1:879007efae624d2b1307214b24a956f0a8d686a8
+zdd1 sha256:eecfdd4d8092dd0363fb6d4548b54c6afc8982c3ed9b34e393f1d6a921d8eaa3
+
+zdm1 sha1:9b541b2275c06e3a7b13f28badf5294e2ae63df4
+zdm1 sha256:ab136e88e19a843c4bf7713d2090d5a2186ba16a6a80dacc12eeddd256a8e556
+
+zdm2 sha1:d77371d15817fcaa57eeec27f770c505ba974ec1
+zdm2 sha256:1c1a5f57363f46a15d95ce8527b3c2c158d88d16853b4acbf81bd20fd2c89a46
+
+zdn1 sha1:beb5d38c55283d280685ea21a0e50cfcc0ca064a
+zdn1 sha256:0f0eca66183617b0aa5ad74b256540329f841470922ca6760263c996d825eb18
+
+zmd1 sha1:d41fda41b7ec4de46b43cb7ea42a45001ae393d5
+zmd1 sha256:1ed32d481852eddf31a0ce12652a0ad14bf5b7a842667b5dbb0b50f35bf1c80a
+
+zmd2 sha1:a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9
+zmd2 sha256:b238da211b404f8917df2d9c6f7030535e904b2186131007a3c292ec6902f933
+
+zmm1 sha1:4ca22bae2527d3d9e1676498a0fba3b355bd1278
+zmm1 sha256:072b1d85b5f34fabc99dfa46008c5418df68302d3e317430006f49b32d244226
+
+zmm2 sha1:61422ba9c2c873416061a88cd40a59a35b576474
+zmm2 sha256:81dd5d2b3c5cda16fef552256aed4e2ea0802a8450a08f308a92142112ff6dda
+
+zmm3 sha1:697aad7715a1e7306ca76290a3dd4208fbaeddfa
+zmm3 sha256:8b10fab49e9be3414aa5e9a93d0e46f9569053440138a7c19a5eb5536d8e95bf
+
+zmn1 sha1:b16d7b25b869f2beb124efa53467d8a1550ad694
+zmn1 sha256:609e4f75d1295e844c826feeba213acb0b6cfc609adfe8ff705b19e3829ae3e9
+
+zmn2 sha1:a5c544c21cfcb07eb80a4d89a5b7d1570002edfd
+zmn2 sha256:d6d03edf2dc1a3b267a8205de5f41a2ff4b03def8c7ae02052b543fb09d589fc
+
+zna1 sha1:d12979c22fff69c59ca9409e7a8fe3ee25eaee80
+zna1 sha256:b37b80e789e8ea32aa323f004628f02013f632124b0282c7fe00a127d3c64c3c
+
+znd1 sha1:a18393c636b98e9bd7296b8b437ea4992b72440c
+znd1 sha256:af92a22eee8c38410a0c9d2b5135a10aeb052cbc7cf675541ed9a67bfcaf7cf9
+
+znm1 sha1:3fdbe17fd013303a2e981e1ca1c6cd6e72789087
+znm1 sha256:f75aeaa0c11e76918e381c105f0752932c6150e941fec565d24fa31098a13dc1
+
+znm2 sha1:7e09d6a3a14bd630913e8c75693cea32157b606d
+znm2 sha256:938d73cfbaa1c902a84fb5b3afd9736aa0590367fb9bd59c6c4d072ce70fcd6d
+EOF
+
+cat >.test-plain-OA <<EOF
+:000000 100644 $(test_oid zero) $(test_oid aa_1) A     AA
+:000000 100644 $(test_oid zero) $(test_oid an_1) A     AN
+:100644 000000 $(test_oid dd_1) $(test_oid zero) D     DD
+:000000 040000 $(test_oid zero) $(test_oid df_1) A     DF
+:100644 000000 $(test_oid dm_1) $(test_oid zero) D     DM
+:100644 000000 $(test_oid dn_1) $(test_oid zero) D     DN
+:000000 100644 $(test_oid zero) $(test_oid ll_2) A     LL
+:100644 100644 $(test_oid md_1) $(test_oid md_2) M     MD
+:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M     MM
+:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M     MN
+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M     SS
+:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M     TT
+:040000 040000 $(test_oid z__1) $(test_oid z__2) M     Z
 EOF
 
-cat >.test-recursive-OA <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 ccba72ad3888a3520b39efcf780b9ee64167535d A     AA
-:000000 100644 0000000000000000000000000000000000000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 A     AN
-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D     DD
-:000000 100644 0000000000000000000000000000000000000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 A     DF/DF
-:100644 000000 141c1f1642328e4bc46a7d801a71da392e66791e 0000000000000000000000000000000000000000 D     DM
-:100644 000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 0000000000000000000000000000000000000000 D     DN
-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A     LL
-:100644 100644 03f24c8c4700babccfd28b654e7e8eac402ad6cd 103d9f89b50b9aad03054b579be5e7aa665f2d57 M     MD
-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 M     MM
-:100644 100644 bd084b0c27c7b6cc34f11d6d0509a29be3caf970 a716d58de4a570e0038f5c307bd8db34daea021f M     MN
-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M     SS
-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 4c86f9a85fbc5e6804ee2e17a797538fbe785bca M     TT
-:000000 100644 0000000000000000000000000000000000000000 8acb8e9750e3f644bf323fcf3d338849db106c77 A     Z/AA
-:000000 100644 0000000000000000000000000000000000000000 087494262084cefee7ed484d20c8dc0580791272 A     Z/AN
-:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D     Z/DD
-:100644 000000 9b541b2275c06e3a7b13f28badf5294e2ae63df4 0000000000000000000000000000000000000000 D     Z/DM
-:100644 000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a 0000000000000000000000000000000000000000 D     Z/DN
-:100644 100644 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 M     Z/MD
-:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 61422ba9c2c873416061a88cd40a59a35b576474 M     Z/MM
-:100644 100644 b16d7b25b869f2beb124efa53467d8a1550ad694 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd M     Z/MN
+cat >.test-recursive-OA <<EOF
+:000000 100644 $(test_oid zero) $(test_oid aa_1) A     AA
+:000000 100644 $(test_oid zero) $(test_oid an_1) A     AN
+:100644 000000 $(test_oid dd_1) $(test_oid zero) D     DD
+:000000 100644 $(test_oid zero) $(test_oid dfd1) A     DF/DF
+:100644 000000 $(test_oid dm_1) $(test_oid zero) D     DM
+:100644 000000 $(test_oid dn_1) $(test_oid zero) D     DN
+:000000 100644 $(test_oid zero) $(test_oid ll_2) A     LL
+:100644 100644 $(test_oid md_1) $(test_oid md_2) M     MD
+:100644 100644 $(test_oid mm_1) $(test_oid mm_2) M     MM
+:100644 100644 $(test_oid mn_1) $(test_oid mn_2) M     MN
+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M     SS
+:100644 100644 $(test_oid tt_1) $(test_oid tt_2) M     TT
+:000000 100644 $(test_oid zero) $(test_oid zaa1) A     Z/AA
+:000000 100644 $(test_oid zero) $(test_oid zan1) A     Z/AN
+:100644 000000 $(test_oid zdd1) $(test_oid zero) D     Z/DD
+:100644 000000 $(test_oid zdm1) $(test_oid zero) D     Z/DM
+:100644 000000 $(test_oid zdn1) $(test_oid zero) D     Z/DN
+:100644 100644 $(test_oid zmd1) $(test_oid zmd2) M     Z/MD
+:100644 100644 $(test_oid zmm1) $(test_oid zmm2) M     Z/MM
+:100644 100644 $(test_oid zmn1) $(test_oid zmn2) M     Z/MN
 EOF
-cat >.test-plain-OB <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A     AA
-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D     DD
-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A     DF
-:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M     DM
-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A     LL
-:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D     MD
-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M     MM
-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A     NA
-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D     ND
-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M     NM
-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M     SS
-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M     TT
-:040000 040000 7d670fdcdb9929f6c7dac196ff78689cd1c566a1 1ba523955d5160681af65cb776411f574c1e8155 M     Z
+cat >.test-plain-OB <<EOF
+:000000 100644 $(test_oid zero) $(test_oid aa_2) A     AA
+:100644 000000 $(test_oid dd_1) $(test_oid zero) D     DD
+:000000 100644 $(test_oid zero) $(test_oid df_2) A     DF
+:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M     DM
+:000000 100644 $(test_oid zero) $(test_oid ll_2) A     LL
+:100644 000000 $(test_oid md_1) $(test_oid zero) D     MD
+:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M     MM
+:000000 100644 $(test_oid zero) $(test_oid na_1) A     NA
+:100644 000000 $(test_oid nd_1) $(test_oid zero) D     ND
+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M     NM
+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M     SS
+:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M     TT
+:040000 040000 $(test_oid z__1) $(test_oid z__3) M     Z
 EOF
-cat >.test-recursive-OB <<\EOF
-:000000 100644 0000000000000000000000000000000000000000 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 A     AA
-:100644 000000 bcc68ef997017466d5c9094bcf7692295f588c9a 0000000000000000000000000000000000000000 D     DD
-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A     DF
-:100644 100644 141c1f1642328e4bc46a7d801a71da392e66791e 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 M     DM
-:000000 100644 0000000000000000000000000000000000000000 1d41122ebdd7a640f29d3c9cc4f9d70094374762 A     LL
-:100644 000000 03f24c8c4700babccfd28b654e7e8eac402ad6cd 0000000000000000000000000000000000000000 D     MD
-:100644 100644 b258508afb7ceb449981bd9d63d2d3e971bf8d34 19989d4559aae417fedee240ccf2ba315ea4dc2b M     MM
-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A     NA
-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D     ND
-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M     NM
-:100644 100644 40c959f984c8b89a2b02520d17f00d717f024397 2ac547ae9614a00d1b28275de608131f7a0e259f M     SS
-:100644 100644 4ac13458899ab908ef3b1128fa378daefc88d356 c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M     TT
-:000000 100644 0000000000000000000000000000000000000000 6c0b99286d0bce551ac4a7b3dff8b706edff3715 A     Z/AA
-:100644 000000 879007efae624d2b1307214b24a956f0a8d686a8 0000000000000000000000000000000000000000 D     Z/DD
-:100644 100644 9b541b2275c06e3a7b13f28badf5294e2ae63df4 d77371d15817fcaa57eeec27f770c505ba974ec1 M     Z/DM
-:100644 000000 d41fda41b7ec4de46b43cb7ea42a45001ae393d5 0000000000000000000000000000000000000000 D     Z/MD
-:100644 100644 4ca22bae2527d3d9e1676498a0fba3b355bd1278 697aad7715a1e7306ca76290a3dd4208fbaeddfa M     Z/MM
-:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A     Z/NA
-:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D     Z/ND
-:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M     Z/NM
+cat >.test-recursive-OB <<EOF
+:000000 100644 $(test_oid zero) $(test_oid aa_2) A     AA
+:100644 000000 $(test_oid dd_1) $(test_oid zero) D     DD
+:000000 100644 $(test_oid zero) $(test_oid df_2) A     DF
+:100644 100644 $(test_oid dm_1) $(test_oid dm_2) M     DM
+:000000 100644 $(test_oid zero) $(test_oid ll_2) A     LL
+:100644 000000 $(test_oid md_1) $(test_oid zero) D     MD
+:100644 100644 $(test_oid mm_1) $(test_oid mm_3) M     MM
+:000000 100644 $(test_oid zero) $(test_oid na_1) A     NA
+:100644 000000 $(test_oid nd_1) $(test_oid zero) D     ND
+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M     NM
+:100644 100644 $(test_oid ss_1) $(test_oid ss_2) M     SS
+:100644 100644 $(test_oid tt_1) $(test_oid tt_3) M     TT
+:000000 100644 $(test_oid zero) $(test_oid zaa2) A     Z/AA
+:100644 000000 $(test_oid zdd1) $(test_oid zero) D     Z/DD
+:100644 100644 $(test_oid zdm1) $(test_oid zdm2) M     Z/DM
+:100644 000000 $(test_oid zmd1) $(test_oid zero) D     Z/MD
+:100644 100644 $(test_oid zmm1) $(test_oid zmm3) M     Z/MM
+:000000 100644 $(test_oid zero) $(test_oid zna1) A     Z/NA
+:100644 000000 $(test_oid znd1) $(test_oid zero) D     Z/ND
+:100644 100644 $(test_oid znm1) $(test_oid znm2) M     Z/NM
 EOF
-cat >.test-plain-AB <<\EOF
-:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M     AA
-:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D     AN
-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A     DF
-:040000 000000 6d50f65d3bdab91c63444294d38f08aeff328e42 0000000000000000000000000000000000000000 D     DF
-:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A     DM
-:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A     DN
-:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D     MD
-:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M     MM
-:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M     MN
-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A     NA
-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D     ND
-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M     NM
-:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M     TT
-:040000 040000 5e5f22072bb39f6e12cf663a57cb634c76eefb49 1ba523955d5160681af65cb776411f574c1e8155 M     Z
+cat >.test-plain-AB <<EOF
+:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M     AA
+:100644 000000 $(test_oid an_1) $(test_oid zero) D     AN
+:000000 100644 $(test_oid zero) $(test_oid df_2) A     DF
+:040000 000000 $(test_oid df_1) $(test_oid zero) D     DF
+:000000 100644 $(test_oid zero) $(test_oid dm_2) A     DM
+:000000 100644 $(test_oid zero) $(test_oid dn_1) A     DN
+:100644 000000 $(test_oid md_2) $(test_oid zero) D     MD
+:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M     MM
+:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M     MN
+:000000 100644 $(test_oid zero) $(test_oid na_1) A     NA
+:100644 000000 $(test_oid nd_1) $(test_oid zero) D     ND
+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M     NM
+:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M     TT
+:040000 040000 $(test_oid z__2) $(test_oid z__3) M     Z
 EOF
-cat >.test-recursive-AB <<\EOF
-:100644 100644 ccba72ad3888a3520b39efcf780b9ee64167535d 6aa2b5335b16431a0ef71e5c0a28be69183cf6a2 M     AA
-:100644 000000 7e426fb079479fd67f6d81f984e4ec649a44bc25 0000000000000000000000000000000000000000 D     AN
-:000000 100644 0000000000000000000000000000000000000000 71420ab81e254145d26d6fc0cddee64c1acd4787 A     DF
-:100644 000000 68a6d8b91da11045cf4aa3a5ab9f2a781c701249 0000000000000000000000000000000000000000 D     DF/DF
-:000000 100644 0000000000000000000000000000000000000000 3c4d8de5fbad08572bab8e10eef8dbb264cf0231 A     DM
-:000000 100644 0000000000000000000000000000000000000000 35abde1506ddf806572ff4d407bd06885d0f8ee9 A     DN
-:100644 000000 103d9f89b50b9aad03054b579be5e7aa665f2d57 0000000000000000000000000000000000000000 D     MD
-:100644 100644 b431b272d829ff3aa4d1a5085f4394ab4d3305b6 19989d4559aae417fedee240ccf2ba315ea4dc2b M     MM
-:100644 100644 a716d58de4a570e0038f5c307bd8db34daea021f bd084b0c27c7b6cc34f11d6d0509a29be3caf970 M     MN
-:000000 100644 0000000000000000000000000000000000000000 15885881ea69115351c09b38371f0348a3fb8c67 A     NA
-:100644 000000 a4e179e4291e5536a5e1c82e091052772d2c5a93 0000000000000000000000000000000000000000 D     ND
-:100644 100644 c8f25781e8f1792e3e40b74225e20553041b5226 cdb9a8c3da571502ac30225e9c17beccb8387983 M     NM
-:100644 100644 4c86f9a85fbc5e6804ee2e17a797538fbe785bca c4e4a12231b9fa79a0053cb6077fcb21bb5b135a M     TT
-:100644 100644 8acb8e9750e3f644bf323fcf3d338849db106c77 6c0b99286d0bce551ac4a7b3dff8b706edff3715 M     Z/AA
-:100644 000000 087494262084cefee7ed484d20c8dc0580791272 0000000000000000000000000000000000000000 D     Z/AN
-:000000 100644 0000000000000000000000000000000000000000 d77371d15817fcaa57eeec27f770c505ba974ec1 A     Z/DM
-:000000 100644 0000000000000000000000000000000000000000 beb5d38c55283d280685ea21a0e50cfcc0ca064a A     Z/DN
-:100644 000000 a79ac3be9377639e1c7d1edf1ae1b3a5f0ccd8a9 0000000000000000000000000000000000000000 D     Z/MD
-:100644 100644 61422ba9c2c873416061a88cd40a59a35b576474 697aad7715a1e7306ca76290a3dd4208fbaeddfa M     Z/MM
-:100644 100644 a5c544c21cfcb07eb80a4d89a5b7d1570002edfd b16d7b25b869f2beb124efa53467d8a1550ad694 M     Z/MN
-:000000 100644 0000000000000000000000000000000000000000 d12979c22fff69c59ca9409e7a8fe3ee25eaee80 A     Z/NA
-:100644 000000 a18393c636b98e9bd7296b8b437ea4992b72440c 0000000000000000000000000000000000000000 D     Z/ND
-:100644 100644 3fdbe17fd013303a2e981e1ca1c6cd6e72789087 7e09d6a3a14bd630913e8c75693cea32157b606d M     Z/NM
+cat >.test-recursive-AB <<EOF
+:100644 100644 $(test_oid aa_1) $(test_oid aa_2) M     AA
+:100644 000000 $(test_oid an_1) $(test_oid zero) D     AN
+:000000 100644 $(test_oid zero) $(test_oid df_2) A     DF
+:100644 000000 $(test_oid dfd1) $(test_oid zero) D     DF/DF
+:000000 100644 $(test_oid zero) $(test_oid dm_2) A     DM
+:000000 100644 $(test_oid zero) $(test_oid dn_1) A     DN
+:100644 000000 $(test_oid md_2) $(test_oid zero) D     MD
+:100644 100644 $(test_oid mm_2) $(test_oid mm_3) M     MM
+:100644 100644 $(test_oid mn_2) $(test_oid mn_1) M     MN
+:000000 100644 $(test_oid zero) $(test_oid na_1) A     NA
+:100644 000000 $(test_oid nd_1) $(test_oid zero) D     ND
+:100644 100644 $(test_oid nm_1) $(test_oid nm_2) M     NM
+:100644 100644 $(test_oid tt_2) $(test_oid tt_3) M     TT
+:100644 100644 $(test_oid zaa1) $(test_oid zaa2) M     Z/AA
+:100644 000000 $(test_oid zan1) $(test_oid zero) D     Z/AN
+:000000 100644 $(test_oid zero) $(test_oid zdm2) A     Z/DM
+:000000 100644 $(test_oid zero) $(test_oid zdn1) A     Z/DN
+:100644 000000 $(test_oid zmd2) $(test_oid zero) D     Z/MD
+:100644 100644 $(test_oid zmm2) $(test_oid zmm3) M     Z/MM
+:100644 100644 $(test_oid zmn2) $(test_oid zmn1) M     Z/MN
+:000000 100644 $(test_oid zero) $(test_oid zna1) A     Z/NA
+:100644 000000 $(test_oid znd1) $(test_oid zero) D     Z/ND
+:100644 100644 $(test_oid znm1) $(test_oid znm2) M     Z/NM
 EOF
 
 cmp_diff_files_output () {
index 3641fd84d68612786ab55beb780209c89311e105..b63bdf031f5186a9c482bbd08a9d95bd3beb1906 100755 (executable)
@@ -14,6 +14,7 @@ test_expect_success \
     'cat "$TEST_DIRECTORY"/diff-lib/COPYING >COPYING &&
      echo frotz >rezrov &&
     git update-index --add COPYING rezrov &&
+    orig=$(git hash-object COPYING) &&
     tree=$(git write-tree) &&
     echo $tree'
 
@@ -22,6 +23,8 @@ test_expect_success \
     'sed -e 's/HOWEVER/However/' <COPYING >COPYING.1 &&
     sed -e 's/GPL/G.P.L/g' <COPYING >COPYING.2 &&
     rm -f COPYING &&
+    c1=$(git hash-object COPYING.1) &&
+    c2=$(git hash-object COPYING.2) &&
     git update-index --add --remove COPYING COPYING.?'
 
 # tree has COPYING and rezrov.  work tree has COPYING.1 and COPYING.2,
@@ -31,11 +34,11 @@ test_expect_success \
 
 git diff-index -z -C $tree >current
 
-cat >expected <<\EOF
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+cat >expected <<EOF
+:100644 100644 $orig $c1 C1234
 COPYING
 COPYING.1
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 R1234
+:100644 100644 $orig $c2 R1234
 COPYING
 COPYING.2
 EOF
@@ -57,10 +60,10 @@ test_expect_success \
 # about rezrov.
 
 git diff-index -z -C $tree >current
-cat >expected <<\EOF
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 06c67961bbaed34a127f76d261f4c0bf73eda471 M
+cat >expected <<EOF
+:100644 100644 $orig $c2 M
 COPYING
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+:100644 100644 $orig $c1 C1234
 COPYING
 COPYING.1
 EOF
@@ -82,8 +85,8 @@ test_expect_success \
      git update-index --add --remove COPYING COPYING.1'
 
 git diff-index -z -C --find-copies-harder $tree >current
-cat >expected <<\EOF
-:100644 100644 6ff87c4664981e4397625791c8ea3bbb5f2279a3 0603b3238a076dc6c8022aedc6648fa523a17178 C1234
+cat >expected <<EOF
+:100644 100644 $orig $c1 C1234
 COPYING
 COPYING.1
 EOF
index a9054d2db11608d7ae2d407e9422a4f61c5e9afd..5ac94b390dfca31bcce585900c2c9b98b795309b 100755 (executable)
@@ -7,9 +7,6 @@ test_description='Various diff formatting options'
 
 . ./test-lib.sh
 
-LF='
-'
-
 test_expect_success setup '
 
        GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" &&
index ca7debf1d4c0b5f73301b2f8a35490b77c167a61..83f52614d32a7a077d9d4070a8652415de869cc7 100755 (executable)
@@ -9,7 +9,6 @@ test_description='various format-patch tests'
 . "$TEST_DIRECTORY"/lib-terminal.sh
 
 test_expect_success setup '
-
        for i in 1 2 3 4 5 6 7 8 9 10; do echo "$i"; done >file &&
        cat file >elif &&
        git add file elif &&
@@ -34,7 +33,8 @@ test_expect_success setup '
        git commit -m "Side changes #3 with \\n backslash-n in it." &&
 
        git checkout master &&
-       git diff-tree -p C2 | git apply --index &&
+       git diff-tree -p C2 >patch &&
+       git apply --index <patch &&
        test_tick &&
        git commit -m "Master accepts moral equivalent of #2" &&
 
@@ -59,33 +59,28 @@ test_expect_success setup '
        git checkout master
 '
 
-test_expect_success "format-patch --ignore-if-in-upstream" '
-
+test_expect_success 'format-patch --ignore-if-in-upstream' '
        git format-patch --stdout master..side >patch0 &&
-       cnt=$(grep "^From " patch0 | wc -l) &&
-       test $cnt = 3
-
+       grep "^From " patch0 >from0 &&
+       test_line_count = 3 from0
 '
 
-test_expect_success "format-patch --ignore-if-in-upstream" '
-
+test_expect_success 'format-patch --ignore-if-in-upstream' '
        git format-patch --stdout \
                --ignore-if-in-upstream master..side >patch1 &&
-       cnt=$(grep "^From " patch1 | wc -l) &&
-       test $cnt = 2
-
+       grep "^From " patch1 >from1 &&
+       test_line_count = 2 from1
 '
 
-test_expect_success "format-patch --ignore-if-in-upstream handles tags" '
+test_expect_success 'format-patch --ignore-if-in-upstream handles tags' '
        git tag -a v1 -m tag side &&
        git tag -a v2 -m tag master &&
        git format-patch --stdout --ignore-if-in-upstream v2..v1 >patch1 &&
-       cnt=$(grep "^From " patch1 | wc -l) &&
-       test $cnt = 2
+       grep "^From " patch1 >from1 &&
+       test_line_count = 2 from1
 '
 
 test_expect_success "format-patch doesn't consider merge commits" '
-
        git checkout -b slave master &&
        echo "Another line" >>file &&
        test_tick &&
@@ -96,148 +91,138 @@ test_expect_success "format-patch doesn't consider merge commits" '
        git checkout -b merger master &&
        test_tick &&
        git merge --no-ff slave &&
-       cnt=$(git format-patch -3 --stdout | grep "^From " | wc -l) &&
-       test $cnt = 3
+       git format-patch -3 --stdout >patch &&
+       grep "^From " patch >from &&
+       test_line_count = 3 from
 '
 
-test_expect_success "format-patch result applies" '
-
+test_expect_success 'format-patch result applies' '
        git checkout -b rebuild-0 master &&
        git am -3 patch0 &&
-       cnt=$(git rev-list master.. | wc -l) &&
-       test $cnt = 2
+       git rev-list master.. >list &&
+       test_line_count = 2 list
 '
 
-test_expect_success "format-patch --ignore-if-in-upstream result applies" '
-
+test_expect_success 'format-patch --ignore-if-in-upstream result applies' '
        git checkout -b rebuild-1 master &&
        git am -3 patch1 &&
-       cnt=$(git rev-list master.. | wc -l) &&
-       test $cnt = 2
+       git rev-list master.. >list &&
+       test_line_count = 2 list
 '
 
 test_expect_success 'commit did not screw up the log message' '
-
-       git cat-file commit side | grep "^Side .* with .* backslash-n"
-
+       git cat-file commit side >actual &&
+       grep "^Side .* with .* backslash-n" actual
 '
 
 test_expect_success 'format-patch did not screw up the log message' '
-
        grep "^Subject: .*Side changes #3 with .* backslash-n" patch0 &&
        grep "^Subject: .*Side changes #3 with .* backslash-n" patch1
-
 '
 
 test_expect_success 'replay did not screw up the log message' '
-
-       git cat-file commit rebuild-1 | grep "^Side .* with .* backslash-n"
-
+       git cat-file commit rebuild-1 >actual &&
+       grep "^Side .* with .* backslash-n" actual
 '
 
 test_expect_success 'extra headers' '
-
        git config format.headers "To: R E Cipient <rcipient@example.com>
 " &&
        git config --add format.headers "Cc: S E Cipient <scipient@example.com>
 " &&
-       git format-patch --stdout master..side > patch2 &&
-       sed -e "/^\$/q" patch2 > hdrs2 &&
+       git format-patch --stdout master..side >patch2 &&
+       sed -e "/^\$/q" patch2 >hdrs2 &&
        grep "^To: R E Cipient <rcipient@example.com>\$" hdrs2 &&
        grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs2
-
 '
 
 test_expect_success 'extra headers without newlines' '
-
        git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
        git config --add format.headers "Cc: S E Cipient <scipient@example.com>" &&
        git format-patch --stdout master..side >patch3 &&
-       sed -e "/^\$/q" patch3 > hdrs3 &&
+       sed -e "/^\$/q" patch3 >hdrs3 &&
        grep "^To: R E Cipient <rcipient@example.com>\$" hdrs3 &&
        grep "^Cc: S E Cipient <scipient@example.com>\$" hdrs3
-
 '
 
 test_expect_success 'extra headers with multiple To:s' '
-
        git config --replace-all format.headers "To: R E Cipient <rcipient@example.com>" &&
        git config --add format.headers "To: S E Cipient <scipient@example.com>" &&
-       git format-patch --stdout master..side > patch4 &&
-       sed -e "/^\$/q" patch4 > hdrs4 &&
+       git format-patch --stdout master..side >patch4 &&
+       sed -e "/^\$/q" patch4 >hdrs4 &&
        grep "^To: R E Cipient <rcipient@example.com>,\$" hdrs4 &&
        grep "^ *S E Cipient <scipient@example.com>\$" hdrs4
 '
 
 test_expect_success 'additional command line cc (ascii)' '
-
        git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-       git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
-       grep "^ *S E Cipient <scipient@example.com>\$" patch5
+       git format-patch --cc="S E Cipient <scipient@example.com>" --stdout master..side >patch5 &&
+       sed -e "/^\$/q" patch5 >hdrs5 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 &&
+       grep "^ *S E Cipient <scipient@example.com>\$" hdrs5
 '
 
 test_expect_failure 'additional command line cc (rfc822)' '
-
        git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-       git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch5 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch5 &&
-       grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" patch5
+       git format-patch --cc="S. E. Cipient <scipient@example.com>" --stdout master..side >patch5 &&
+       sed -e "/^\$/q" patch5 >hdrs5 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs5 &&
+       grep "^ *\"S. E. Cipient\" <scipient@example.com>\$" hdrs5
 '
 
 test_expect_success 'command line headers' '
-
        git config --unset-all format.headers &&
-       git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch6 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>\$" patch6
+       git format-patch --add-header="Cc: R E Cipient <rcipient@example.com>" --stdout master..side >patch6 &&
+       sed -e "/^\$/q" patch6 >hdrs6 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>\$" hdrs6
 '
 
 test_expect_success 'configuration headers and command line headers' '
-
        git config --replace-all format.headers "Cc: R E Cipient <rcipient@example.com>" &&
-       git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch7 &&
-       grep "^Cc: R E Cipient <rcipient@example.com>,\$" patch7 &&
-       grep "^ *S E Cipient <scipient@example.com>\$" patch7
+       git format-patch --add-header="Cc: S E Cipient <scipient@example.com>" --stdout master..side >patch7 &&
+       sed -e "/^\$/q" patch7 >hdrs7 &&
+       grep "^Cc: R E Cipient <rcipient@example.com>,\$" hdrs7 &&
+       grep "^ *S E Cipient <scipient@example.com>\$" hdrs7
 '
 
 test_expect_success 'command line To: header (ascii)' '
-
        git config --unset-all format.headers &&
-       git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
-       grep "^To: R E Cipient <rcipient@example.com>\$" patch8
+       git format-patch --to="R E Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+       sed -e "/^\$/q" patch8 >hdrs8 &&
+       grep "^To: R E Cipient <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_failure 'command line To: header (rfc822)' '
-
-       git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
-       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch8
+       git format-patch --to="R. E. Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+       sed -e "/^\$/q" patch8 >hdrs8 &&
+       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_failure 'command line To: header (rfc2047)' '
-
-       git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side | sed -e "/^\$/q" >patch8 &&
-       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch8
+       git format-patch --to="R Ä Cipient <rcipient@example.com>" --stdout master..side >patch8 &&
+       sed -e "/^\$/q" patch8 >hdrs8 &&
+       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs8
 '
 
 test_expect_success 'configuration To: header (ascii)' '
-
        git config format.to "R E Cipient <rcipient@example.com>" &&
-       git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
-       grep "^To: R E Cipient <rcipient@example.com>\$" patch9
+       git format-patch --stdout master..side >patch9 &&
+       sed -e "/^\$/q" patch9 >hdrs9 &&
+       grep "^To: R E Cipient <rcipient@example.com>\$" hdrs9
 '
 
 test_expect_failure 'configuration To: header (rfc822)' '
-
        git config format.to "R. E. Cipient <rcipient@example.com>" &&
-       git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
-       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" patch9
+       git format-patch --stdout master..side >patch9 &&
+       sed -e "/^\$/q" patch9 >hdrs9 &&
+       grep "^To: \"R. E. Cipient\" <rcipient@example.com>\$" hdrs9
 '
 
 test_expect_failure 'configuration To: header (rfc2047)' '
-
        git config format.to "R Ä Cipient <rcipient@example.com>" &&
-       git format-patch --stdout master..side | sed -e "/^\$/q" >patch9 &&
-       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" patch9
+       git format-patch --stdout master..side >patch9 &&
+       sed -e "/^\$/q" patch9 >hdrs9 &&
+       grep "^To: =?UTF-8?q?R=20=C3=84=20Cipient?= <rcipient@example.com>\$" hdrs9
 '
 
 # check_patch <patch>: Verify that <patch> looks like a half-sane
@@ -249,89 +234,79 @@ check_patch () {
 }
 
 test_expect_success 'format.from=false' '
-
-       git -c format.from=false format-patch --stdout master..side |
-       sed -e "/^\$/q" >patch &&
+       git -c format.from=false format-patch --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
        check_patch patch &&
-       ! grep "^From: C O Mitter <committer@example.com>\$" patch
+       ! grep "^From: C O Mitter <committer@example.com>\$" hdrs
 '
 
 test_expect_success 'format.from=true' '
-
-       git -c format.from=true format-patch --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       grep "^From: C O Mitter <committer@example.com>\$" patch
+       git -c format.from=true format-patch --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       grep "^From: C O Mitter <committer@example.com>\$" hdrs
 '
 
 test_expect_success 'format.from with address' '
-
-       git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       grep "^From: F R Om <from@example.com>\$" patch
+       git -c format.from="F R Om <from@example.com>" format-patch --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--no-from overrides format.from' '
-
-       git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       ! grep "^From: F R Om <from@example.com>\$" patch
+       git -c format.from="F R Om <from@example.com>" format-patch --no-from --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       ! grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--from overrides format.from' '
-
-       git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side |
-       sed -e "/^\$/q" >patch &&
-       check_patch patch &&
-       ! grep "^From: F R Om <from@example.com>\$" patch
+       git -c format.from="F R Om <from@example.com>" format-patch --from --stdout master..side >patch &&
+       sed -e "/^\$/q" patch >hdrs &&
+       check_patch hdrs &&
+       ! grep "^From: F R Om <from@example.com>\$" hdrs
 '
 
 test_expect_success '--no-to overrides config.to' '
-
        git config --replace-all format.to \
                "R E Cipient <rcipient@example.com>" &&
-       git format-patch --no-to --stdout master..side |
-       sed -e "/^\$/q" >patch10 &&
-       check_patch patch10 &&
-       ! grep "^To: R E Cipient <rcipient@example.com>\$" patch10
+       git format-patch --no-to --stdout master..side >patch10 &&
+       sed -e "/^\$/q" patch10 >hdrs10 &&
+       check_patch hdrs10 &&
+       ! grep "^To: R E Cipient <rcipient@example.com>\$" hdrs10
 '
 
 test_expect_success '--no-to and --to replaces config.to' '
-
        git config --replace-all format.to \
                "Someone <someone@out.there>" &&
        git format-patch --no-to --to="Someone Else <else@out.there>" \
-               --stdout master..side |
-       sed -e "/^\$/q" >patch11 &&
-       check_patch patch11 &&
-       ! grep "^To: Someone <someone@out.there>\$" patch11 &&
-       grep "^To: Someone Else <else@out.there>\$" patch11
+               --stdout master..side >patch11 &&
+       sed -e "/^\$/q" patch11 >hdrs11 &&
+       check_patch hdrs11 &&
+       ! grep "^To: Someone <someone@out.there>\$" hdrs11 &&
+       grep "^To: Someone Else <else@out.there>\$" hdrs11
 '
 
 test_expect_success '--no-cc overrides config.cc' '
-
        git config --replace-all format.cc \
                "C E Cipient <rcipient@example.com>" &&
-       git format-patch --no-cc --stdout master..side |
-       sed -e "/^\$/q" >patch12 &&
-       check_patch patch12 &&
-       ! grep "^Cc: C E Cipient <rcipient@example.com>\$" patch12
+       git format-patch --no-cc --stdout master..side >patch12 &&
+       sed -e "/^\$/q" patch12 >hdrs12 &&
+       check_patch hdrs12 &&
+       ! grep "^Cc: C E Cipient <rcipient@example.com>\$" hdrs12
 '
 
 test_expect_success '--no-add-header overrides config.headers' '
-
        git config --replace-all format.headers \
                "Header1: B E Cipient <rcipient@example.com>" &&
-       git format-patch --no-add-header --stdout master..side |
-       sed -e "/^\$/q" >patch13 &&
-       check_patch patch13 &&
-       ! grep "^Header1: B E Cipient <rcipient@example.com>\$" patch13
+       git format-patch --no-add-header --stdout master..side >patch13 &&
+       sed -e "/^\$/q" patch13 >hdrs13 &&
+       check_patch hdrs13 &&
+       ! grep "^Header1: B E Cipient <rcipient@example.com>\$" hdrs13
 '
 
 test_expect_success 'multiple files' '
-
        rm -rf patches/ &&
        git checkout side &&
        git format-patch -o patches/ master &&
@@ -357,7 +332,7 @@ test_expect_success 'reroll count (-v)' '
 check_threading () {
        expect="$1" &&
        shift &&
-       (git format-patch --stdout "$@"; echo $? > status.out) |
+       git format-patch --stdout "$@" >patch &&
        # Prints everything between the Message-ID and In-Reply-To,
        # and replaces all Message-ID-lookalikes by a sequence number
        perl -ne '
@@ -372,12 +347,11 @@ check_threading () {
                        print;
                }
                print "---\n" if /^From /i;
-       ' > actual &&
-       test 0 = "$(cat status.out)" &&
+       ' <patch >actual &&
        test_cmp "$expect" actual
 }
 
-cat >> expect.no-threading <<EOF
+cat >>expect.no-threading <<EOF
 ---
 ---
 ---
@@ -388,7 +362,7 @@ test_expect_success 'no threading' '
        check_threading expect.no-threading master
 '
 
-cat > expect.thread <<EOF
+cat >expect.thread <<EOF
 ---
 Message-Id: <0>
 ---
@@ -405,7 +379,7 @@ test_expect_success 'thread' '
        check_threading expect.thread --thread master
 '
 
-cat > expect.in-reply-to <<EOF
+cat >expect.in-reply-to <<EOF
 ---
 Message-Id: <0>
 In-Reply-To: <1>
@@ -425,7 +399,7 @@ test_expect_success 'thread in-reply-to' '
                --thread master
 '
 
-cat > expect.cover-letter <<EOF
+cat >expect.cover-letter <<EOF
 ---
 Message-Id: <0>
 ---
@@ -446,7 +420,7 @@ test_expect_success 'thread cover-letter' '
        check_threading expect.cover-letter --cover-letter --thread master
 '
 
-cat > expect.cl-irt <<EOF
+cat >expect.cl-irt <<EOF
 ---
 Message-Id: <0>
 In-Reply-To: <1>
@@ -478,7 +452,7 @@ test_expect_success 'thread explicit shallow' '
                --in-reply-to="<test.message>" --thread=shallow master
 '
 
-cat > expect.deep <<EOF
+cat >expect.deep <<EOF
 ---
 Message-Id: <0>
 ---
@@ -496,7 +470,7 @@ test_expect_success 'thread deep' '
        check_threading expect.deep --thread=deep master
 '
 
-cat > expect.deep-irt <<EOF
+cat >expect.deep-irt <<EOF
 ---
 Message-Id: <0>
 In-Reply-To: <1>
@@ -519,7 +493,7 @@ test_expect_success 'thread deep in-reply-to' '
                --in-reply-to="<test.message>" master
 '
 
-cat > expect.deep-cl <<EOF
+cat >expect.deep-cl <<EOF
 ---
 Message-Id: <0>
 ---
@@ -543,7 +517,7 @@ test_expect_success 'thread deep cover-letter' '
        check_threading expect.deep-cl --cover-letter --thread=deep master
 '
 
-cat > expect.deep-cl-irt <<EOF
+cat >expect.deep-cl-irt <<EOF
 ---
 Message-Id: <0>
 In-Reply-To: <1>
@@ -594,7 +568,6 @@ test_expect_success 'thread config + --no-thread' '
 '
 
 test_expect_success 'excessive subject' '
-
        rm -rf patches/ &&
        git checkout side &&
        before=$(git hash-object file) &&
@@ -622,10 +595,9 @@ test_expect_success 'cover-letter inherits diff options' '
        ! grep "file => foo .* 0 *\$" 0000-cover-letter.patch &&
        git format-patch --cover-letter -1 -M &&
        grep "file => foo .* 0 *\$" 0000-cover-letter.patch
-
 '
 
-cat > expect << EOF
+cat >expect <<EOF
   This is an excessively long subject line for a message due to the
     habit some projects have of not having a short, one-line subject at
     the start of the commit message, but rather sticking a whole
@@ -636,14 +608,12 @@ cat > expect << EOF
 EOF
 
 test_expect_success 'shortlog of cover-letter wraps overly-long onelines' '
-
        git format-patch --cover-letter -2 &&
-       sed -e "1,/A U Thor/d" -e "/^\$/q" < 0000-cover-letter.patch > output &&
+       sed -e "1,/A U Thor/d" -e "/^\$/q" 0000-cover-letter.patch >output &&
        test_cmp expect output
-
 '
 
-cat > expect << EOF
+cat >expect <<EOF
 index $before..$after 100644
 --- a/file
 +++ b/file
@@ -656,16 +626,14 @@ index $before..$after 100644
 EOF
 
 test_expect_success 'format-patch respects -U' '
-
        git format-patch -U4 -2 &&
        sed -e "1,/^diff/d" -e "/^+5/q" \
                <0001-This-is-an-excessively-long-subject-line-for-a-messa.patch \
                >output &&
        test_cmp expect output
-
 '
 
-cat > expect << EOF
+cat >expect <<EOF
 
 diff --git a/file b/file
 index $before..$after 100644
@@ -679,11 +647,9 @@ index $before..$after 100644
 EOF
 
 test_expect_success 'format-patch -p suppresses stat' '
-
        git format-patch -p -2 &&
-       sed -e "1,/^\$/d" -e "/^+5/q" < 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch > output &&
+       sed -e "1,/^\$/d" -e "/^+5/q" 0001-This-is-an-excessively-long-subject-line-for-a-messa.patch >output &&
        test_cmp expect output
-
 '
 
 test_expect_success 'format-patch from a subdirectory (1)' '
@@ -736,7 +702,7 @@ test_expect_success 'format-patch from a subdirectory (3)' '
 '
 
 test_expect_success 'format-patch --in-reply-to' '
-       git format-patch -1 --stdout --in-reply-to "baz@foo.bar" > patch8 &&
+       git format-patch -1 --stdout --in-reply-to "baz@foo.bar" >patch8 &&
        grep "^In-Reply-To: <baz@foo.bar>" patch8 &&
        grep "^References: <baz@foo.bar>" patch8
 '
@@ -827,21 +793,24 @@ test_expect_success 'format-patch with multiple notes refs' '
        ! grep "this is note 2" out
 '
 
-echo "fatal: --name-only does not make sense" > expect.name-only
-echo "fatal: --name-status does not make sense" > expect.name-status
-echo "fatal: --check does not make sense" > expect.check
+echo "fatal: --name-only does not make sense" >expect.name-only
+echo "fatal: --name-status does not make sense" >expect.name-status
+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_must_fail git format-patch --name-only 2>output &&
        test_i18ncmp expect.name-only output &&
-       test_must_fail git format-patch --name-status 2> output &&
+       test_must_fail git format-patch --name-status 2>output &&
        test_i18ncmp expect.name-status output &&
-       test_must_fail git format-patch --check 2> output &&
-       test_i18ncmp expect.check output'
+       test_must_fail git format-patch --check 2>output &&
+       test_i18ncmp expect.check output
+'
 
 test_expect_success 'format-patch --numstat should produce a patch' '
-       git format-patch --numstat --stdout master..side > output &&
-       test 5 = $(grep "^diff --git a/" output | wc -l)'
+       git format-patch --numstat --stdout master..side >output &&
+       grep "^diff --git a/" output >diff &&
+       test_line_count = 5 diff
+'
 
 test_expect_success 'format-patch -- <path>' '
        git format-patch master..side -- file 2>error &&
@@ -852,20 +821,25 @@ test_expect_success 'format-patch --ignore-if-in-upstream HEAD' '
        git format-patch --ignore-if-in-upstream HEAD
 '
 
-git_version="$(git --version | sed "s/.* //")"
+test_expect_success 'get git version' '
+       git_version=$(git --version) &&
+       git_version=${git_version##* }
+'
 
 signature() {
        printf "%s\n%s\n\n" "-- " "${1:-$git_version}"
 }
 
 test_expect_success 'format-patch default signature' '
-       git format-patch --stdout -1 | tail -n 3 >output &&
+       git format-patch --stdout -1 >patch &&
+       tail -n 3 patch >output &&
        signature >expect &&
        test_cmp expect output
 '
 
 test_expect_success 'format-patch --signature' '
-       git format-patch --stdout --signature="my sig" -1 | tail -n 3 >output &&
+       git format-patch --stdout --signature="my sig" -1 >patch &&
+       tail -n 3 patch >output &&
        signature "my sig" >expect &&
        test_cmp expect output
 '
@@ -897,8 +871,8 @@ test_expect_success 'format-patch --signature --cover-letter' '
        git config --unset-all format.signature &&
        git format-patch --stdout --signature="my sig" --cover-letter \
                -1 >output &&
-       grep "my sig" output &&
-       test 2 = $(grep "my sig" output | wc -l)
+       grep "my sig" output >sig &&
+       test_line_count = 2 sig
 '
 
 test_expect_success 'format.signature="" suppresses signatures' '
@@ -935,7 +909,7 @@ test_expect_success 'prepare mail-signature input' '
 test_expect_success '--signature-file=file works' '
        git format-patch --stdout --signature-file=mail-signature -1 >output &&
        check_patch output &&
-       sed -e "1,/^-- \$/d" <output >actual &&
+       sed -e "1,/^-- \$/d" output >actual &&
        {
                cat mail-signature && echo
        } >expect &&
@@ -946,7 +920,7 @@ test_expect_success 'format.signaturefile works' '
        test_config format.signaturefile mail-signature &&
        git format-patch --stdout -1 >output &&
        check_patch output &&
-       sed -e "1,/^-- \$/d" <output >actual &&
+       sed -e "1,/^-- \$/d" output >actual &&
        {
                cat mail-signature && echo
        } >expect &&
@@ -968,7 +942,7 @@ test_expect_success '--signature-file overrides format.signaturefile' '
        git format-patch --stdout \
                        --signature-file=other-mail-signature -1 >output &&
        check_patch output &&
-       sed -e "1,/^-- \$/d" <output >actual &&
+       sed -e "1,/^-- \$/d" output >actual &&
        {
                cat other-mail-signature && echo
        } >expect &&
@@ -1037,7 +1011,7 @@ test_expect_success 'format-patch wraps extremely long subject (ascii)' '
        git add file &&
        git commit -m "$M512" &&
        git format-patch --stdout -1 >patch &&
-       sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+       sed -n "/^Subject/p; /^ /p; /^$/q" patch >subject &&
        test_cmp expect subject
 '
 
@@ -1076,7 +1050,7 @@ test_expect_success 'format-patch wraps extremely long subject (rfc2047)' '
        git add file &&
        git commit -m "$M512" &&
        git format-patch --stdout -1 >patch &&
-       sed -n "/^Subject/p; /^ /p; /^$/q" <patch >subject &&
+       sed -n "/^Subject/p; /^ /p; /^$/q" patch >subject &&
        test_cmp expect subject
 '
 
@@ -1085,7 +1059,7 @@ check_author() {
        git add file &&
        GIT_AUTHOR_NAME=$1 git commit -m author-check &&
        git format-patch --stdout -1 >patch &&
-       sed -n "/^From: /p; /^ /p; /^$/q" <patch >actual &&
+       sed -n "/^From: /p; /^ /p; /^$/q" patch >actual &&
        test_cmp expect actual
 }
 
@@ -1205,7 +1179,7 @@ test_expect_success '--from=ident replaces author' '
        From: A U Thor <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1217,7 +1191,7 @@ test_expect_success '--from uses committer ident' '
        From: A U Thor <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1227,7 +1201,7 @@ test_expect_success '--from omits redundant in-body header' '
        From: A U Thor <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1242,7 +1216,7 @@ test_expect_success 'in-body headers trigger content encoding' '
        From: éxötìc <author@example.com>
 
        EOF
-       sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" <patch >patch.head &&
+       sed -ne "/^From:/p; /^$/p; /^Content-Type/p; /^---$/q" patch >patch.head &&
        test_cmp expect patch.head
 '
 
@@ -1256,283 +1230,283 @@ append_signoff()
 
 test_expect_success 'signoff: commit with no body' '
        append_signoff </dev/null >actual &&
-       cat <<\EOF | sed "s/EOL$//" >expected &&
-4:Subject: [PATCH] EOL
-8:
-9:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       cat <<-\EOF | sed "s/EOL$//" >expect &&
+       4:Subject: [PATCH] EOL
+       8:
+       9:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: commit with only subject' '
        echo subject | append_signoff >actual &&
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-9:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       9:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: commit with only subject that does not end with NL' '
        printf subject | append_signoff >actual &&
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-9:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       9:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: no existing signoffs' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-11:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       body
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       11:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: no existing signoffs and no trailing NL' '
        printf "subject\n\nbody" | append_signoff >actual &&
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-11:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       11:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: some random signoff' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
+       body
 
-Signed-off-by: my@house
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-11:Signed-off-by: my@house
-12:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       Signed-off-by: my@house
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       11:Signed-off-by: my@house
+       12:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: misc conforming footer elements' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
+       body
 
-Signed-off-by: my@house
-(cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
-Tested-by: Some One <someone@example.com>
-Bug: 1234
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-11:Signed-off-by: my@house
-15:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       Signed-off-by: my@house
+       (cherry picked from commit da39a3ee5e6b4b0d3255bfef95601890afd80709)
+       Tested-by: Some One <someone@example.com>
+       Bug: 1234
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       11:Signed-off-by: my@house
+       15:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: some random signoff-alike' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
-Fooled-by-me: my@house
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-11:
-12:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       body
+       Fooled-by-me: my@house
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       11:
+       12:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: not really a signoff' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-I want to mention about Signed-off-by: here.
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-9:I want to mention about Signed-off-by: here.
-10:
-11:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       I want to mention about Signed-off-by: here.
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       9:I want to mention about Signed-off-by: here.
+       10:
+       11:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: not really a signoff (2)' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-My unfortunate
-Signed-off-by: example happens to be wrapped here.
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:Signed-off-by: example happens to be wrapped here.
-11:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       My unfortunate
+       Signed-off-by: example happens to be wrapped here.
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:Signed-off-by: example happens to be wrapped here.
+       11:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: valid S-o-b paragraph in the middle' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-Signed-off-by: my@house
-Signed-off-by: your@house
+       Signed-off-by: my@house
+       Signed-off-by: your@house
 
-A lot of houses.
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-9:Signed-off-by: my@house
-10:Signed-off-by: your@house
-11:
-13:
-14:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       A lot of houses.
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       9:Signed-off-by: my@house
+       10:Signed-off-by: your@house
+       11:
+       13:
+       14:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: the same signoff at the end' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
+       body
 
-Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-11:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       11:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: the same signoff at the end, no trailing NL' '
        printf "subject\n\nSigned-off-by: C O Mitter <committer@example.com>" |
                append_signoff >actual &&
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-9:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       9:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: the same signoff NOT at the end' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
+       body
 
-Signed-off-by: C O Mitter <committer@example.com>
-Signed-off-by: my@house
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-11:Signed-off-by: C O Mitter <committer@example.com>
-12:Signed-off-by: my@house
-EOF
-       test_cmp expected actual
+       Signed-off-by: C O Mitter <committer@example.com>
+       Signed-off-by: my@house
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       11:Signed-off-by: C O Mitter <committer@example.com>
+       12:Signed-off-by: my@house
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: tolerate garbage in conforming footer' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
+       body
 
-Tested-by: my@house
-Some Trash
-Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-13:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       Tested-by: my@house
+       Some Trash
+       Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       13:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: respect trailer config' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-Myfooter: x
-Some Trash
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-11:
-12:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual &&
+       Myfooter: x
+       Some Trash
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       11:
+       12:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual &&
 
        test_config trailer.Myfooter.ifexists add &&
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-Myfooter: x
-Some Trash
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-11:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       Myfooter: x
+       Some Trash
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       11:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'signoff: footer begins with non-signoff without @ sign' '
-       append_signoff <<\EOF >actual &&
-subject
+       append_signoff <<-\EOF >actual &&
+       subject
 
-body
+       body
 
-Reviewed-id: Noone
-Tested-by: my@house
-Change-id: Ideadbeef
-Signed-off-by: C O Mitter <committer@example.com>
-Bug: 1234
-EOF
-       cat >expected <<\EOF &&
-4:Subject: [PATCH] subject
-8:
-10:
-14:Signed-off-by: C O Mitter <committer@example.com>
-EOF
-       test_cmp expected actual
+       Reviewed-id: Noone
+       Tested-by: my@house
+       Change-id: Ideadbeef
+       Signed-off-by: C O Mitter <committer@example.com>
+       Bug: 1234
+       EOF
+       cat >expect <<-\EOF &&
+       4:Subject: [PATCH] subject
+       8:
+       10:
+       14:Signed-off-by: C O Mitter <committer@example.com>
+       EOF
+       test_cmp expect actual
 '
 
 test_expect_success 'format patch ignores color.ui' '
@@ -1547,42 +1521,42 @@ test_expect_success 'cover letter using branch description (1)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter master >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (2)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter rebuild-1~2..rebuild-1 >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (3)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter ^master rebuild-1 >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (4)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter master.. >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (5)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter -2 HEAD >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter using branch description (6)' '
        git checkout rebuild-1 &&
        test_config branch.rebuild-1.description hello &&
        git format-patch --stdout --cover-letter -2 >actual &&
-       grep hello actual >/dev/null
+       grep hello actual
 '
 
 test_expect_success 'cover letter with nothing' '
@@ -1636,7 +1610,8 @@ test_expect_success 'format-patch format.outputDirectory option' '
        test_config format.outputDirectory patches &&
        rm -fr patches &&
        git format-patch master..side &&
-       test $(git rev-list master..side | wc -l) -eq $(ls patches | wc -l)
+       git rev-list master..side >list &&
+       test_line_count = $(ls patches | wc -l) list
 '
 
 test_expect_success 'format-patch -o overrides format.outputDirectory' '
@@ -1649,20 +1624,41 @@ test_expect_success 'format-patch -o overrides format.outputDirectory' '
 
 test_expect_success 'format-patch --base' '
        git checkout patchid &&
-       git format-patch --stdout --base=HEAD~3 -1 | tail -n 7 >actual1 &&
-       git format-patch --stdout --base=HEAD~3 HEAD~.. | tail -n 7 >actual2 &&
-       echo >expected &&
-       echo "base-commit: $(git rev-parse HEAD~3)" >>expected &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --stable | awk "{print \$1}")" >>expected &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --stable | awk "{print \$1}")" >>expected &&
-       signature >> expected &&
-       test_cmp expected actual1 &&
-       test_cmp expected actual2 &&
+
+       git format-patch --stdout --base=HEAD~3 -1 >patch &&
+       tail -n 7 patch >actual1 &&
+
+       git format-patch --stdout --base=HEAD~3 HEAD~.. >patch &&
+       tail -n 7 patch >actual2 &&
+
+       echo >expect &&
+       git rev-parse HEAD~3 >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >>expect &&
+
+       git show --patch HEAD~2 >patch &&
+       git patch-id --stable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>expect &&
+
+       git show --patch HEAD~1 >patch &&
+       git patch-id --stable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>expect &&
+
+       signature >>expect &&
+       test_cmp expect actual1 &&
+       test_cmp expect actual2 &&
+
        echo >fail &&
-       echo "base-commit: $(git rev-parse HEAD~3)" >>fail &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~2 | git patch-id --unstable | awk "{print \$1}")" >>fail &&
-       echo "prerequisite-patch-id: $(git show --patch HEAD~1 | git patch-id --unstable | awk "{print \$1}")" >>fail &&
-       signature >> fail &&
+       echo "base-commit: $(cat commit-id-base)" >>fail &&
+
+       git show --patch HEAD~2 >patch &&
+       git patch-id --unstable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>fail &&
+
+       git show --patch HEAD~1 >patch &&
+       git patch-id --unstable <patch >patch.id.raw &&
+       awk "{print \"prerequisite-patch-id:\", \$1}" <patch.id.raw >>fail &&
+
+       signature >>fail &&
        ! test_cmp fail actual1 &&
        ! test_cmp fail actual2
 '
@@ -1672,8 +1668,9 @@ test_expect_success 'format-patch --base errors out when base commit is in revis
        test_must_fail git format-patch --base=HEAD~1 -2 &&
        git format-patch --stdout --base=HEAD~2 -2 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse HEAD~2)" >expected &&
-       test_cmp expected actual
+       git rev-parse HEAD~2 >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'format-patch --base errors out when base commit is not ancestor of revision list' '
@@ -1699,8 +1696,8 @@ test_expect_success 'format-patch --base errors out when base commit is not ance
        test_must_fail git format-patch --base=$(cat commit-id-Z) -3 &&
        git format-patch --stdout --base=$(cat commit-id-base) -3 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(cat commit-id-base)" >expected &&
-       test_cmp expected actual
+       echo "base-commit: $(cat commit-id-base)" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'format-patch --base=auto' '
@@ -1711,8 +1708,9 @@ test_expect_success 'format-patch --base=auto' '
        test_commit N2 &&
        git format-patch --stdout --base=auto -2 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse upstream)" >expected &&
-       test_cmp expected actual
+       git rev-parse upstream >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'format-patch errors out when history involves criss-cross' '
@@ -1748,8 +1746,9 @@ test_expect_success 'format-patch format.useAutoBaseoption' '
        git config format.useAutoBase true &&
        git format-patch --stdout -1 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse upstream)" >expected &&
-       test_cmp expected actual
+       git rev-parse upstream >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'format-patch --base overrides format.useAutoBase' '
@@ -1757,8 +1756,9 @@ test_expect_success 'format-patch --base overrides format.useAutoBase' '
        git config format.useAutoBase true &&
        git format-patch --stdout --base=HEAD~1 -1 >patch &&
        grep "^base-commit:" patch >actual &&
-       echo "base-commit: $(git rev-parse HEAD~1)" >expected &&
-       test_cmp expected actual
+       git rev-parse HEAD~1 >commit-id-base &&
+       echo "base-commit: $(cat commit-id-base)" >expect &&
+       test_cmp expect actual
 '
 
 test_expect_success 'format-patch --base with --attach' '
@@ -1833,7 +1833,7 @@ test_expect_success 'interdiff: cover-letter' '
        git format-patch --cover-letter --interdiff=boop~2 -1 boop &&
        test_i18ngrep "^Interdiff:$" 0000-cover-letter.patch &&
        test_i18ngrep ! "^Interdiff:$" 0001-fleep.patch &&
-       sed "1,/^@@ /d; /^-- $/q" <0000-cover-letter.patch >actual &&
+       sed "1,/^@@ /d; /^-- $/q" 0000-cover-letter.patch >actual &&
        test_cmp expect actual
 '
 
@@ -1849,7 +1849,7 @@ test_expect_success 'interdiff: solo-patch' '
        EOF
        git format-patch --interdiff=boop~2 -1 boop &&
        test_i18ngrep "^Interdiff:$" 0001-fleep.patch &&
-       sed "1,/^  @@ /d; /^$/q" <0001-fleep.patch >actual &&
+       sed "1,/^  @@ /d; /^$/q" 0001-fleep.patch >actual &&
        test_cmp expect actual
 '
 
index 9261d6d3a0000e9891e1af58265349522ca40b84..6f5ef0035e92998eb74b14081aa021247abf67d8 100755 (executable)
@@ -31,6 +31,7 @@ diffpatterns="
        cpp
        csharp
        css
+       dts
        fortran
        fountain
        golang
diff --git a/t/t4018/dts-labels b/t/t4018/dts-labels
new file mode 100644 (file)
index 0000000..b21ef87
--- /dev/null
@@ -0,0 +1,9 @@
+/ {
+       label_1: node1@ff00 {
+               label2: RIGHT {
+                       vendor,some-property;
+
+                       ChangeMe = <0x45-30>;
+               };
+       };
+};
diff --git a/t/t4018/dts-node-unitless b/t/t4018/dts-node-unitless
new file mode 100644 (file)
index 0000000..c5287d9
--- /dev/null
@@ -0,0 +1,8 @@
+/ {
+       label_1: node1 {
+               RIGHT {
+                       prop-array = <1>, <4>;
+                       ChangeMe = <0xffeedd00>;
+               };
+       };
+};
diff --git a/t/t4018/dts-nodes b/t/t4018/dts-nodes
new file mode 100644 (file)
index 0000000..5a4334b
--- /dev/null
@@ -0,0 +1,8 @@
+/ {
+       label_1: node1@ff00 {
+               RIGHT@deadf00,4000 {
+                       #size-cells = <1>;
+                       ChangeMe = <0xffeedd00>;
+               };
+       };
+};
diff --git a/t/t4018/dts-nodes-comment1 b/t/t4018/dts-nodes-comment1
new file mode 100644 (file)
index 0000000..559dfce
--- /dev/null
@@ -0,0 +1,8 @@
+/ {
+       label_1: node1@ff00 {
+               RIGHT@deadf00,4000 /* &a comment */ {
+                       #size-cells = <1>;
+                       ChangeMe = <0xffeedd00>;
+               };
+       };
+};
diff --git a/t/t4018/dts-nodes-comment2 b/t/t4018/dts-nodes-comment2
new file mode 100644 (file)
index 0000000..27e9718
--- /dev/null
@@ -0,0 +1,8 @@
+/ {
+       label_1: node1@ff00 {
+               RIGHT@deadf00,4000 { /* a trailing comment */ 
+                       #size-cells = <1>;
+                       ChangeMe = <0xffeedd00>;
+               };
+       };
+};
diff --git a/t/t4018/dts-reference b/t/t4018/dts-reference
new file mode 100644 (file)
index 0000000..8f0c87d
--- /dev/null
@@ -0,0 +1,9 @@
+&label_1 {
+       TEST = <455>;
+};
+
+&RIGHT {
+       vendor,some-property;
+
+       ChangeMe = <0x45-30>;
+};
diff --git a/t/t4018/dts-root b/t/t4018/dts-root
new file mode 100644 (file)
index 0000000..2ef9e6f
--- /dev/null
@@ -0,0 +1,5 @@
+/RIGHT { /* Technically just supposed to be a slash */
+       #size-cells = <1>;
+
+       ChangeMe = <0xffeedd00>;
+};
index 912df91226f2a018016129a6634b837faee19869..9a93c2a3e0dd8a4763092549df77172657009c56 100755 (executable)
@@ -303,6 +303,7 @@ test_language_driver bibtex
 test_language_driver cpp
 test_language_driver csharp
 test_language_driver css
+test_language_driver dts
 test_language_driver fortran
 test_language_driver html
 test_language_driver java
diff --git a/t/t4034/dts/expect b/t/t4034/dts/expect
new file mode 100644 (file)
index 0000000..560fc99
--- /dev/null
@@ -0,0 +1,37 @@
+<BOLD>diff --git a/pre b/post<RESET>
+<BOLD>index b6a9051..7803aee 100644<RESET>
+<BOLD>--- a/pre<RESET>
+<BOLD>+++ b/post<RESET>
+<CYAN>@@ -1,32 +1,32 @@<RESET>
+/ {<RESET>
+       <RED>this_handle<RESET><GREEN>HANDLE_2<RESET>: <RED>node<RESET><GREEN>new-node<RESET>@<RED>f00<RESET><GREEN>eeda<RESET> {
+               compatible = "<RED>mydev<RESET><GREEN>vendor,compat<RESET>";
+               string-prop = <RED>start<RESET><GREEN>end<RESET>: "hello <RED>world!<RESET><GREEN>world?<RESET>" <RED>end<RESET><GREEN>start<RESET>: ;
+               <RED>#size-cells<RESET><GREEN>#address-cells<RESET> = <<RED>0+0<RESET><GREEN>0+40<RESET>>;
+               reg = <<RED>0xf00<RESET><GREEN>0xeeda<RESET>>;
+               prop = <<GREEN>(<RESET>1<GREEN>)<RESET>>;
+               prop = <<GREEN>(<RESET>-1e10<GREEN>)<RESET>>;
+               prop = <(!<RED>3<RESET><GREEN>1<RESET>)>;
+               prop = <(~<RED>3<RESET><GREEN>1<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>*<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>&<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>*<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>/<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>%<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3+4<RESET><GREEN>1+2<RESET>)>;
+               prop = <(<RED>3-4<RESET><GREEN>1-2<RESET>)>;
+               prop = /bits/ <RED>64<RESET><GREEN>32<RESET> <(<RED>3<RESET><GREEN>1<RESET><<<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>>><RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>&<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>^<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>|<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>&&<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>3<RESET><GREEN>1<RESET>||<RED>4<RESET><GREEN>2<RESET>)>;
+               prop = <(<RED>4?5<RESET><GREEN>1?2<RESET>:3)>;
+               list = <&<RED>this_handle<RESET><GREEN>HANDLE_2<RESET>>, <0 0 0 <RED>0<RESET><GREEN>1<RESET>>;
+       };<RESET>
+
+       &<RED>phandle<RESET><GREEN>phandle2<RESET> {
+               <RED>pre-phandle<RESET><GREEN>prop_handle<RESET> = <&<RED>this_handle<RESET><GREEN>HANDLE_2<RESET>>;
+       };<RESET>
+};<RESET>
diff --git a/t/t4034/dts/post b/t/t4034/dts/post
new file mode 100644 (file)
index 0000000..7803aee
--- /dev/null
@@ -0,0 +1,32 @@
+/ {
+       HANDLE_2: new-node@eeda {
+               compatible = "vendor,compat";
+               string-prop = end: "hello world?" start: ;
+               #address-cells = <0+40>;
+               reg = <0xeeda>;
+               prop = <(1)>;
+               prop = <(-1e10)>;
+               prop = <(!1)>;
+               prop = <(~1)>;
+               prop = <(1*2)>;
+               prop = <(1&2)>;
+               prop = <(1*2)>;
+               prop = <(1/2)>;
+               prop = <(1%2)>;
+               prop = <(1+2)>;
+               prop = <(1-2)>;
+               prop = /bits/ 32 <(1<<2)>;
+               prop = <(1>>2)>;
+               prop = <(1&2)>;
+               prop = <(1^2)>;
+               prop = <(1|2)>;
+               prop = <(1&&2)>;
+               prop = <(1||2)>;
+               prop = <(1?2:3)>;
+               list = <&HANDLE_2>, <0 0 0 1>;
+       };
+
+       &phandle2 {
+               prop_handle = <&HANDLE_2>;
+       };
+};
diff --git a/t/t4034/dts/pre b/t/t4034/dts/pre
new file mode 100644 (file)
index 0000000..b6a9051
--- /dev/null
@@ -0,0 +1,32 @@
+/ {
+       this_handle: node@f00 {
+               compatible = "mydev";
+               string-prop = start: "hello world!" end: ;
+               #size-cells = <0+0>;
+               reg = <0xf00>;
+               prop = <1>;
+               prop = <-1e10>;
+               prop = <(!3)>;
+               prop = <(~3)>;
+               prop = <(3*4)>;
+               prop = <(3&4)>;
+               prop = <(3*4)>;
+               prop = <(3/4)>;
+               prop = <(3%4)>;
+               prop = <(3+4)>;
+               prop = <(3-4)>;
+               prop = /bits/ 64 <(3<<4)>;
+               prop = <(3>>4)>;
+               prop = <(3&4)>;
+               prop = <(3^4)>;
+               prop = <(3|4)>;
+               prop = <(3&&4)>;
+               prop = <(3||4)>;
+               prop = <(4?5:3)>;
+               list = <&this_handle>, <0 0 0 0>;
+       };
+
+       &phandle {
+               pre-phandle = <&this_handle>;
+       };
+};
index 90c8fb29017562d5341a61a4bb872d323b8f8340..4831ad35e61436fdc400533fb0e7f88e6d27027c 100755 (executable)
@@ -75,6 +75,37 @@ test_expect_success 'diff skips same-OID blobs' '
        ! grep "want $(cat hash-b)" trace
 '
 
+test_expect_success 'when fetching missing objects, diff skips GITLINKs' '
+       test_when_finished "rm -rf sub server client trace" &&
+
+       test_create_repo sub &&
+       test_commit -C sub first &&
+
+       test_create_repo server &&
+       echo a >server/a &&
+       git -C server add a &&
+       git -C server submodule add "file://$(pwd)/sub" &&
+       git -C server commit -m x &&
+
+       test_commit -C server/sub second &&
+       echo another-a >server/a &&
+       git -C server add a sub &&
+       git -C server commit -m x &&
+
+       test_config -C server uploadpack.allowfilter 1 &&
+       test_config -C server uploadpack.allowanysha1inwant 1 &&
+       git clone --bare --filter=blob:limit=0 "file://$(pwd)/server" client &&
+
+       echo a | git hash-object --stdin >hash-old-a &&
+       echo another-a | git hash-object --stdin >hash-new-a &&
+
+       # Ensure that a and another-a are fetched, and check (by successful
+       # execution of the diff) that no invalid OIDs are sent.
+       GIT_TRACE_PACKET="$(pwd)/trace" git -C client diff HEAD^ HEAD &&
+       grep "want $(cat hash-old-a)" trace &&
+       grep "want $(cat hash-new-a)" trace
+'
+
 test_expect_success 'diff with rename detection batches blobs' '
        test_when_finished "rm -rf server client trace" &&
 
index 3f7f750cc8d7442458323f9ec77a371db3818320..4f1e24ecbecd87667251eab612c9b966405d53c6 100755 (executable)
@@ -1061,4 +1061,56 @@ test_expect_success 'am --quit keeps HEAD where it is' '
        test_cmp expected actual
 '
 
+test_expect_success 'am and .gitattibutes' '
+       test_create_repo attributes &&
+       (
+               cd attributes &&
+               test_commit init &&
+               git config filter.test.clean "sed -e '\''s/smudged/clean/g'\''" &&
+               git config filter.test.smudge "sed -e '\''s/clean/smudged/g'\''" &&
+
+               test_commit second &&
+               git checkout -b test HEAD^ &&
+
+               echo "*.txt filter=test conflict-marker-size=10" >.gitattributes &&
+               git add .gitattributes &&
+               test_commit third &&
+
+               echo "This text is smudged." >a.txt &&
+               git add a.txt &&
+               test_commit fourth &&
+
+               git checkout -b removal HEAD^ &&
+               git rm .gitattributes &&
+               git add -u &&
+               test_commit fifth &&
+               git cherry-pick test &&
+
+               git checkout -b conflict third &&
+               echo "This text is different." >a.txt &&
+               git add a.txt &&
+               test_commit sixth &&
+
+               git checkout test &&
+               git format-patch --stdout master..HEAD >patches &&
+               git reset --hard master &&
+               git am patches &&
+               grep "smudged" a.txt &&
+
+               git checkout removal &&
+               git reset --hard &&
+               git format-patch --stdout master..HEAD >patches &&
+               git reset --hard master &&
+               git am patches &&
+               grep "clean" a.txt &&
+
+               git checkout conflict &&
+               git reset --hard &&
+               git format-patch --stdout master..HEAD >patches &&
+               git reset --hard fourth &&
+               test_must_fail git am -3 patches &&
+               grep "<<<<<<<<<<" a.txt
+       )
+'
+
 test_done
index c20209324c8e71c677b70ce2217cf6439385024b..e88ccb04a9715b92e47976e66753c38c2547babf 100755 (executable)
@@ -1707,4 +1707,11 @@ test_expect_success '--exclude-promisor-objects does not BUG-crash' '
        test_must_fail git log --exclude-promisor-objects source-a
 '
 
+test_expect_success 'log --end-of-options' '
+       git update-ref refs/heads/--source HEAD &&
+       git log --end-of-options --source >actual &&
+       git log >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 1db7bd0f59b46768d6559c37e026cde41821763a..83191637441437b1c0b086e9417087940a221162 100755 (executable)
@@ -132,4 +132,86 @@ test_expect_success '--raw is forbidden' '
        test_must_fail git log -L1,24:b.c --raw
 '
 
+test_expect_success 'setup for checking fancy rename following' '
+       git checkout --orphan moves-start &&
+       git reset --hard &&
+
+       printf "%s\n"    12 13 14 15      b c d e   >file-1 &&
+       printf "%s\n"    22 23 24 25      B C D E   >file-2 &&
+       git add file-1 file-2 &&
+       test_tick &&
+       git commit -m "Add file-1 and file-2" &&
+       oid_add_f1_f2=$(git rev-parse --short HEAD) &&
+
+       git checkout -b moves-main &&
+       printf "%s\n" 11 12 13 14 15      b c d e   >file-1 &&
+       git commit -a -m "Modify file-1 on main" &&
+       oid_mod_f1_main=$(git rev-parse --short HEAD) &&
+
+       printf "%s\n" 21 22 23 24 25      B C D E   >file-2 &&
+       git commit -a -m "Modify file-2 on main #1" &&
+       oid_mod_f2_main_1=$(git rev-parse --short HEAD) &&
+
+       git mv file-1 renamed-1 &&
+       git commit -m "Rename file-1 to renamed-1 on main" &&
+
+       printf "%s\n" 11 12 13 14 15      b c d e f >renamed-1 &&
+       git commit -a -m "Modify renamed-1 on main" &&
+       oid_mod_r1_main=$(git rev-parse --short HEAD) &&
+
+       printf "%s\n" 21 22 23 24 25      B C D E F >file-2 &&
+       git commit -a -m "Modify file-2 on main #2" &&
+       oid_mod_f2_main_2=$(git rev-parse --short HEAD) &&
+
+       git checkout -b moves-side moves-start &&
+       printf "%s\n"    12 13 14 15 16   b c d e   >file-1 &&
+       git commit -a -m "Modify file-1 on side #1" &&
+       oid_mod_f1_side_1=$(git rev-parse --short HEAD) &&
+
+       printf "%s\n"    22 23 24 25 26   B C D E   >file-2 &&
+       git commit -a -m "Modify file-2 on side" &&
+       oid_mod_f2_side=$(git rev-parse --short HEAD) &&
+
+       git mv file-2 renamed-2 &&
+       git commit -m "Rename file-2 to renamed-2 on side" &&
+
+       printf "%s\n"    12 13 14 15 16 a b c d e   >file-1 &&
+       git commit -a -m "Modify file-1 on side #2" &&
+       oid_mod_f1_side_2=$(git rev-parse --short HEAD) &&
+
+       printf "%s\n"    22 23 24 25 26 A B C D E   >renamed-2 &&
+       git commit -a -m "Modify renamed-2 on side" &&
+       oid_mod_r2_side=$(git rev-parse --short HEAD) &&
+
+       git checkout moves-main &&
+       git merge moves-side &&
+       oid_merge=$(git rev-parse --short HEAD)
+'
+
+test_expect_success 'fancy rename following #1' '
+       cat >expect <<-EOF &&
+       $oid_merge Merge branch '\''moves-side'\'' into moves-main
+       $oid_mod_f1_side_2 Modify file-1 on side #2
+       $oid_mod_f1_side_1 Modify file-1 on side #1
+       $oid_mod_r1_main Modify renamed-1 on main
+       $oid_mod_f1_main Modify file-1 on main
+       $oid_add_f1_f2 Add file-1 and file-2
+       EOF
+       git log -L1:renamed-1 --oneline --no-patch >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fancy rename following #2' '
+       cat >expect <<-EOF &&
+       $oid_merge Merge branch '\''moves-side'\'' into moves-main
+       $oid_mod_r2_side Modify renamed-2 on side
+       $oid_mod_f2_side Modify file-2 on side
+       $oid_mod_f2_main_2 Modify file-2 on main #2
+       $oid_mod_f2_main_1 Modify file-2 on main #1
+       $oid_add_f1_f2 Add file-1 and file-2
+       EOF
+       git log -L1:renamed-2 --oneline --no-patch >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 271eb5a1fdfbfe4aab216271fc1897968283639d..3e7b23cb32c581b88473b596036517a38c51c9fd 100755 (executable)
@@ -204,4 +204,23 @@ test_expect_success EXPENSIVE,LONG_IS_64BIT,UNZIP,UNZIP_ZIP64_SUPPORT,ZIPINFO \
        grep $size big.lst
 '
 
+build_tree() {
+       perl -e '
+               my $hash = $ARGV[0];
+               foreach my $order (2..6) {
+                       $first = 10 ** $order;
+                       foreach my $i (-13..-9) {
+                               my $name = "a" x ($first + $i);
+                               print "100644 blob $hash\t$name\n"
+                       }
+               }
+       ' "$1"
+}
+
+test_expect_success 'tar archive with long paths' '
+       blob=$(echo foo | git hash-object -w --stdin) &&
+       tree=$(build_tree $blob | git mktree) &&
+       git archive -o long_paths.tar $tree
+'
+
 test_done
index dacb440b2750e40b393a200229a9ee968f12ccd3..f4338abb78a83967a1bdc6b264600262a0f94d65 100755 (executable)
@@ -24,11 +24,11 @@ test_expect_success 'check corruption' '
 '
 
 test_expect_success 'rev-list notices corruption (1)' '
-       test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list HEAD
+       test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list HEAD
 '
 
 test_expect_success 'rev-list notices corruption (2)' '
-       test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --objects HEAD
+       test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list --objects HEAD
 '
 
 test_expect_success 'pack-objects notices corruption' '
index 99f4ef4c19dff028facac6da810c973b3a82a4fc..e2c39533d3f8ce292f151d82f19dd753b564b882 100755 (executable)
@@ -8,6 +8,7 @@ GIT_TEST_COMMIT_GRAPH=0
 test_expect_success 'setup repo' '
        git init &&
        git config core.commitGraph true &&
+       git config gc.writeCommitGraph false &&
        infodir=".git/objects/info" &&
        graphdir="$infodir/commit-graphs" &&
        test_oid_init
@@ -334,6 +335,7 @@ test_expect_success 'split across alternate where alternate is not split' '
        git clone --no-hardlinks . alt-split &&
        (
                cd alt-split &&
+               rm -f .git/objects/info/commit-graph &&
                echo "$(pwd)"/../.git/objects >.git/objects/info/alternates &&
                test_commit 18 &&
                git commit-graph write --reachable --split &&
index e55d8474efb6891d4bea9edf5488f793b26e135a..961eb35c99db6ba4d740f707cd17c7357a3aad0a 100755 (executable)
@@ -12,9 +12,6 @@ GIT_TEST_PROTOCOL_VERSION=
 
 . ./test-lib.sh
 
-LF='
-'
-
 test_expect_success setup '
        GIT_AUTHOR_DATE="2006-06-26 00:00:00 +0000" &&
        GIT_COMMITTER_DATE="2006-06-26 00:00:00 +0000" &&
index 8a14be51a13280d3fccd340c027cd5f20bb941a5..f70cbcc9cabf6499de57529cbfa095ed9eff73e3 100755 (executable)
@@ -60,29 +60,6 @@ test_expect_success 'commits with no parents are sent regardless of skip distanc
        have_not_sent c6 c4 c3
 '
 
-test_expect_success 'unknown fetch.negotiationAlgorithm values error out' '
-       rm -rf server client trace &&
-       git init server &&
-       test_commit -C server to_fetch &&
-
-       git init client &&
-       test_commit -C client on_client &&
-       git -C client checkout on_client &&
-
-       test_config -C client fetch.negotiationAlgorithm invalid &&
-       test_must_fail git -C client fetch "$(pwd)/server" 2>err &&
-       test_i18ngrep "unknown fetch negotiation algorithm" err &&
-
-       # Explicit "default" value
-       test_config -C client fetch.negotiationAlgorithm default &&
-       git -C client -c fetch.negotiationAlgorithm=default fetch "$(pwd)/server" &&
-
-       # Implementation detail: If there is nothing to fetch, we will not error out
-       test_config -C client fetch.negotiationAlgorithm invalid &&
-       git -C client fetch "$(pwd)/server" 2>err &&
-       test_i18ngrep ! "unknown fetch negotiation algorithm" err
-'
-
 test_expect_success 'when two skips collide, favor the larger one' '
        rm -rf server client trace &&
        git init server &&
diff --git a/t/t5553-set-upstream.sh b/t/t5553-set-upstream.sh
new file mode 100755 (executable)
index 0000000..81975ad
--- /dev/null
@@ -0,0 +1,178 @@
+#!/bin/sh
+
+test_description='"git fetch/pull --set-upstream" basic tests.'
+. ./test-lib.sh
+
+check_config () {
+       printf "%s\n" "$2" "$3" >"expect.$1" &&
+       {
+               git config "branch.$1.remote" && git config "branch.$1.merge"
+       } >"actual.$1" &&
+       test_cmp "expect.$1" "actual.$1"
+}
+
+check_config_missing () {
+       test_expect_code 1 git config "branch.$1.remote" &&
+       test_expect_code 1 git config "branch.$1.merge"
+}
+
+clear_config () {
+       for branch in "$@"; do
+               test_might_fail git config --unset-all "branch.$branch.remote"
+               test_might_fail git config --unset-all "branch.$branch.merge"
+       done
+}
+
+ensure_fresh_upstream () {
+       rm -rf parent && git init --bare parent
+}
+
+test_expect_success 'setup bare parent fetch' '
+       ensure_fresh_upstream &&
+       git remote add upstream parent
+'
+
+test_expect_success 'setup commit on master and other fetch' '
+       test_commit one &&
+       git push upstream master &&
+       git checkout -b other &&
+       test_commit two &&
+       git push upstream other
+'
+
+# tests for fetch --set-upstream
+
+test_expect_success 'fetch --set-upstream does not set upstream w/o branch' '
+       clear_config master other &&
+       git checkout master &&
+       git fetch --set-upstream upstream &&
+       check_config_missing master &&
+       check_config_missing other
+'
+
+test_expect_success 'fetch --set-upstream upstream master sets branch master but not other' '
+       clear_config master other &&
+       git fetch --set-upstream upstream master &&
+       check_config master upstream refs/heads/master &&
+       check_config_missing other
+'
+
+test_expect_success 'fetch --set-upstream upstream other sets branch other' '
+       clear_config master other &&
+       git fetch --set-upstream upstream other &&
+       check_config master upstream refs/heads/other &&
+       check_config_missing other
+'
+
+test_expect_success 'fetch --set-upstream master:other does not set the branch other2' '
+       clear_config other2 &&
+       git fetch --set-upstream upstream master:other2 &&
+       check_config_missing other2
+'
+
+test_expect_success 'fetch --set-upstream http://nosuchdomain.example.com fails with invalid url' '
+       # master explicitly not cleared, we check that it is not touched from previous value
+       clear_config other other2 &&
+       test_must_fail git fetch --set-upstream http://nosuchdomain.example.com &&
+       check_config master upstream refs/heads/other &&
+       check_config_missing other &&
+       check_config_missing other2
+'
+
+test_expect_success 'fetch --set-upstream with valid URL sets upstream to URL' '
+       clear_config other other2 &&
+       url="file://'"$PWD"'" &&
+       git fetch --set-upstream "$url" &&
+       check_config master "$url" HEAD &&
+       check_config_missing other &&
+       check_config_missing other2
+'
+
+# tests for pull --set-upstream
+
+test_expect_success 'setup bare parent pull' '
+       git remote rm upstream &&
+       ensure_fresh_upstream &&
+       git remote add upstream parent
+'
+
+test_expect_success 'setup commit on master and other pull' '
+       test_commit three &&
+       git push --tags upstream master &&
+       test_commit four &&
+       git push upstream other
+'
+
+test_expect_success 'pull --set-upstream upstream master sets branch master but not other' '
+       clear_config master other &&
+       git pull --set-upstream upstream master &&
+       check_config master upstream refs/heads/master &&
+       check_config_missing other
+'
+
+test_expect_success 'pull --set-upstream master:other2 does not set the branch other2' '
+       clear_config other2 &&
+       git pull --set-upstream upstream master:other2 &&
+       check_config_missing other2
+'
+
+test_expect_success 'pull --set-upstream upstream other sets branch master' '
+       clear_config master other &&
+       git pull --set-upstream upstream other &&
+       check_config master upstream refs/heads/other &&
+       check_config_missing other
+'
+
+test_expect_success 'pull --set-upstream upstream tag does not set the tag' '
+       clear_config three &&
+       git pull --tags --set-upstream upstream three &&
+       check_config_missing three
+'
+
+test_expect_success 'pull --set-upstream http://nosuchdomain.example.com fails with invalid url' '
+       # master explicitly not cleared, we check that it is not touched from previous value
+       clear_config other other2 three &&
+       test_must_fail git pull --set-upstream http://nosuchdomain.example.com &&
+       check_config master upstream refs/heads/other &&
+       check_config_missing other &&
+       check_config_missing other2 &&
+       check_config_missing three
+'
+
+test_expect_success 'pull --set-upstream upstream HEAD sets branch HEAD' '
+       clear_config master other &&
+       git pull --set-upstream upstream HEAD &&
+       check_config master upstream HEAD &&
+       git checkout other &&
+       git pull --set-upstream upstream HEAD &&
+       check_config other upstream HEAD
+'
+
+test_expect_success 'pull --set-upstream upstream with more than one branch does nothing' '
+       clear_config master three &&
+       git pull --set-upstream upstream master three &&
+       check_config_missing master &&
+       check_config_missing three
+'
+
+test_expect_success 'pull --set-upstream with valid URL sets upstream to URL' '
+       clear_config master other other2 &&
+       git checkout master &&
+       url="file://'"$PWD"'" &&
+       git pull --set-upstream "$url" &&
+       check_config master "$url" HEAD &&
+       check_config_missing other &&
+       check_config_missing other2
+'
+
+test_expect_success 'pull --set-upstream with valid URL and branch sets branch' '
+       clear_config master other other2 &&
+       git checkout master &&
+       url="file://'"$PWD"'" &&
+       git pull --set-upstream "$url" master &&
+       check_config master "$url" refs/heads/master &&
+       check_config_missing other &&
+       check_config_missing other2
+'
+
+test_done
index 4a3b901f067c2cb024247f2dc9299a6215939019..2e94354e5b11b3dfcf5104e4911cb37003a1c03e 100755 (executable)
@@ -654,7 +654,8 @@ partial_clone () {
        git -C client fsck &&
 
        # Ensure that unneeded blobs are not inadvertently fetched.
-       test_config -C client extensions.partialclone "not a remote" &&
+       test_config -C client remote.origin.promisor "false" &&
+       git -C client config --unset remote.origin.partialclonefilter &&
        test_must_fail git -C client cat-file -e "$HASH1" &&
 
        # But this blob was fetched, because clone performs an initial checkout
index 2a0fb15cf175251df0a140aa4f5c56fd7885292d..b7a3fdf02dfb4096133d1aca8de412d907ea921b 100755 (executable)
@@ -83,4 +83,15 @@ test_expect_success 'failed bundle creation does not leave cruft' '
        test_path_is_missing fail.bundle.lock
 '
 
+test_expect_success 'fetch SHA-1 from bundle' '
+       test_create_repo foo &&
+       test_commit -C foo x &&
+       git -C foo bundle create tip.bundle -1 master &&
+       git -C foo rev-parse HEAD >hash &&
+
+       # Exercise to ensure that fetching a SHA-1 from a bundle works with no
+       # errors
+       git fetch --no-tags foo/tip.bundle "$(cat hash)"
+'
+
 test_done
index 565254558f362397270bcf13bd3566993e1b0f50..fc634a56b2076283198060de467e3a4c8ce3ffa6 100755 (executable)
@@ -42,8 +42,8 @@ test_expect_success 'do partial clone 1' '
 
        test_cmp expect_1.oids observed.oids &&
        test "$(git -C pc1 config --local core.repositoryformatversion)" = "1" &&
-       test "$(git -C pc1 config --local extensions.partialclone)" = "origin" &&
-       test "$(git -C pc1 config --local core.partialclonefilter)" = "blob:none"
+       test "$(git -C pc1 config --local remote.origin.promisor)" = "true" &&
+       test "$(git -C pc1 config --local remote.origin.partialclonefilter)" = "blob:none"
 '
 
 # checkout master to force dynamic object fetch of blobs at HEAD.
@@ -208,6 +208,25 @@ test_expect_success 'use fsck before and after manually fetching a missing subtr
        test_cmp unique_types.expected unique_types.observed
 '
 
+test_expect_success 'implicitly construct combine: filter with repeated flags' '
+       GIT_TRACE=$(pwd)/trace git clone --bare \
+               --filter=blob:none --filter=tree:1 \
+               "file://$(pwd)/srv.bare" pc2 &&
+       grep "trace:.* git pack-objects .*--filter=combine:blob:none+tree:1" \
+               trace &&
+       git -C pc2 rev-list --objects --missing=allow-any HEAD >objects &&
+
+       # We should have gotten some root trees.
+       grep " $" objects &&
+       # Should not have gotten any non-root trees or blobs.
+       ! grep " ." objects &&
+
+       xargs -n 1 git -C pc2 cat-file -t <objects >types &&
+       sort -u types >unique_types.actual &&
+       test_write_lines commit tree >unique_types.expected &&
+       test_cmp unique_types.expected unique_types.actual
+'
+
 test_expect_success 'partial clone fetches blobs pointed to by refs even if normally filtered out' '
        rm -rf src dst &&
        git init src &&
index fbddd0aea9340827899c766c7235226f5a4823a6..ae9175cedfb2b5e51acc92ec797014e6459e3c9a 100755 (executable)
@@ -631,6 +631,19 @@ test_expect_success 'fetch with http:// using protocol v2' '
        grep "git< version 2" log
 '
 
+test_expect_success 'fetch with http:// by hash without tag following with protocol v2 does not list refs' '
+       test_when_finished "rm -f log" &&
+
+       test_commit -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" two_a &&
+       git -C "$HTTPD_DOCUMENT_ROOT_PATH/http_parent" rev-parse two_a >two_a_hash &&
+
+       GIT_TRACE_PACKET="$(pwd)/log" git -C http_child -c protocol.version=2 \
+               fetch --no-tags origin $(cat two_a_hash) &&
+
+       grep "fetch< version 2" log &&
+       ! grep "fetch> command=ls-refs" log
+'
+
 test_expect_success 'fetch from namespaced repo respects namespaces' '
        test_when_finished "rm -f log" &&
 
index 52a9e38d66f3222f4c02f7d067a9509db685f875..b8cf82349b1d6d0405342cf98ead948d2e81c2a5 100755 (executable)
@@ -140,4 +140,12 @@ test_expect_success '--header shows a NUL after each commit' '
        test_cmp expect actual
 '
 
+test_expect_success 'rev-list --end-of-options' '
+       git update-ref refs/heads/--output=yikes HEAD &&
+       git rev-list --end-of-options --output=yikes >actual &&
+       test_path_is_missing yikes &&
+       git rev-list HEAD >expect &&
+       test_cmp expect actual
+'
+
 test_done
index 545b461e51d4fd5df39c7d27953fb0f26f73e052..bad02cf5b83dbc014c23cc8ac66135e0cf095138 100755 (executable)
@@ -42,7 +42,7 @@ test_expect_success 'corrupt second commit object' \
    '
 
 test_expect_success 'rev-list should fail' '
-       test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git rev-list --all > /dev/null
+       test_must_fail env GIT_TEST_COMMIT_GRAPH=0 git -c core.commitGraph=false rev-list --all > /dev/null
 '
 
 test_expect_success 'git repack _MUST_ fail' \
index acd7f5ab80d9c8a78c915ec9b868e704237841c5..de0e5a5d3646cdd7e60b98b566bb075200f311ad 100755 (executable)
@@ -278,7 +278,19 @@ test_expect_success 'verify skipping tree iteration when not collecting omits' '
        test_line_count = 2 actual &&
 
        # Make sure no other trees were considered besides the root.
-       ! grep "Skipping contents of tree [^.]" filter_trace
+       ! grep "Skipping contents of tree [^.]" filter_trace &&
+
+       # Try this again with "combine:". If both sub-filters are skipping
+       # trees, the composite filter should also skip trees. This is not
+       # important unless the user does combine:tree:X+tree:Y or another filter
+       # besides "tree:" is implemented in the future which can skip trees.
+       GIT_TRACE=1 git -C r3 rev-list \
+               --objects --filter=combine:tree:1+tree:3 HEAD 2>filter_trace &&
+
+       # Only skip the dir1/ tree, which is shared between the two commits.
+       grep "Skipping contents of tree " filter_trace >actual &&
+       test_write_lines "Skipping contents of tree dir1/..." >expected &&
+       test_cmp expected actual
 '
 
 # Test tree:# filters.
@@ -330,6 +342,148 @@ test_expect_success 'verify tree:3 includes everything expected' '
        test_line_count = 10 actual
 '
 
+test_expect_success 'combine:... for a simple combination' '
+       git -C r3 rev-list --objects --filter=combine:tree:2+blob:none HEAD \
+               >actual &&
+
+       expect_has HEAD "" &&
+       expect_has HEAD~1 "" &&
+       expect_has HEAD dir1 &&
+
+       # There are also 2 commit objects
+       test_line_count = 5 actual &&
+
+       cp actual expected &&
+
+       # Try again using repeated --filter - this is equivalent to a manual
+       # combine with "combine:...+..."
+       git -C r3 rev-list --objects --filter=combine:tree:2 \
+               --filter=blob:none HEAD >actual &&
+
+       test_cmp expected actual
+'
+
+test_expect_success 'combine:... with URL encoding' '
+       git -C r3 rev-list --objects \
+               --filter=combine:tree%3a2+blob:%6Eon%65 HEAD >actual &&
+
+       expect_has HEAD "" &&
+       expect_has HEAD~1 "" &&
+       expect_has HEAD dir1 &&
+
+       # There are also 2 commit objects
+       test_line_count = 5 actual
+'
+
+expect_invalid_filter_spec () {
+       spec="$1" &&
+       err="$2" &&
+
+       test_must_fail git -C r3 rev-list --objects --filter="$spec" HEAD \
+               >actual 2>actual_stderr &&
+       test_must_be_empty actual &&
+       test_i18ngrep "$err" actual_stderr
+}
+
+test_expect_success 'combine:... while URL-encoding things that should not be' '
+       expect_invalid_filter_spec combine%3Atree:2+blob:none \
+               "invalid filter-spec"
+'
+
+test_expect_success 'combine: with nothing after the :' '
+       expect_invalid_filter_spec combine: "expected something after combine:"
+'
+
+test_expect_success 'parse error in first sub-filter in combine:' '
+       expect_invalid_filter_spec combine:tree:asdf+blob:none \
+               "expected .tree:<depth>."
+'
+
+test_expect_success 'combine:... with non-encoded reserved chars' '
+       expect_invalid_filter_spec combine:tree:2+sparse:@xyz \
+               "must escape char in sub-filter-spec: .@." &&
+       expect_invalid_filter_spec combine:tree:2+sparse:\` \
+               "must escape char in sub-filter-spec: .\`." &&
+       expect_invalid_filter_spec combine:tree:2+sparse:~abc \
+               "must escape char in sub-filter-spec: .\~."
+'
+
+test_expect_success 'validate err msg for "combine:<valid-filter>+"' '
+       expect_invalid_filter_spec combine:tree:2+ "expected .tree:<depth>."
+'
+
+test_expect_success 'combine:... with edge-case hex digits: Ff Aa 0 9' '
+       git -C r3 rev-list --objects --filter="combine:tree:2+bl%6Fb:n%6fne" \
+               HEAD >actual &&
+       test_line_count = 5 actual &&
+       git -C r3 rev-list --objects --filter="combine:tree%3A2+blob%3anone" \
+               HEAD >actual &&
+       test_line_count = 5 actual &&
+       git -C r3 rev-list --objects --filter="combine:tree:%30" HEAD >actual &&
+       test_line_count = 2 actual &&
+       git -C r3 rev-list --objects --filter="combine:tree:%39+blob:none" \
+               HEAD >actual &&
+       test_line_count = 5 actual
+'
+
+test_expect_success 'add sparse pattern blobs whose paths have reserved chars' '
+       cp r3/pattern r3/pattern1+renamed% &&
+       cp r3/pattern "r3/p;at%ter+n" &&
+       cp r3/pattern r3/^~pattern &&
+       git -C r3 add pattern1+renamed% "p;at%ter+n" ^~pattern &&
+       git -C r3 commit -m "add sparse pattern files with reserved chars"
+'
+
+test_expect_success 'combine:... with more than two sub-filters' '
+       git -C r3 rev-list --objects \
+               --filter=combine:tree:3+blob:limit=40+sparse:oid=master:pattern \
+               HEAD >actual &&
+
+       expect_has HEAD "" &&
+       expect_has HEAD~1 "" &&
+       expect_has HEAD~2 "" &&
+       expect_has HEAD dir1 &&
+       expect_has HEAD dir1/sparse1 &&
+       expect_has HEAD dir1/sparse2 &&
+
+       # Should also have 3 commits
+       test_line_count = 9 actual &&
+
+       # Try again, this time making sure the last sub-filter is only
+       # URL-decoded once.
+       cp actual expect &&
+
+       git -C r3 rev-list --objects \
+               --filter=combine:tree:3+blob:limit=40+sparse:oid=master:pattern1%2brenamed%25 \
+               HEAD >actual &&
+       test_cmp expect actual &&
+
+       # Use the same composite filter again, but with a pattern file name that
+       # requires encoding multiple characters, and use implicit filter
+       # combining.
+       test_when_finished "rm -f trace1" &&
+       GIT_TRACE=$(pwd)/trace1 git -C r3 rev-list --objects \
+               --filter=tree:3 --filter=blob:limit=40 \
+               --filter=sparse:oid="master:p;at%ter+n" \
+               HEAD >actual &&
+
+       test_cmp expect actual &&
+       grep "Add to combine filter-spec: sparse:oid=master:p%3bat%25ter%2bn" \
+               trace1 &&
+
+       # Repeat the above test, but this time, the characters to encode are in
+       # the LHS of the combined filter.
+       test_when_finished "rm -f trace2" &&
+       GIT_TRACE=$(pwd)/trace2 git -C r3 rev-list --objects \
+               --filter=sparse:oid=master:^~pattern \
+               --filter=tree:3 --filter=blob:limit=40 \
+               HEAD >actual &&
+
+       test_cmp expect actual &&
+       grep "Add to combine filter-spec: sparse:oid=master:%5e%7epattern" \
+               trace2
+'
+
 # Test provisional omit collection logic with a repo that has objects appearing
 # at multiple depths - first deeper than the filter's threshold, then shallow.
 
@@ -373,6 +527,37 @@ test_expect_success 'verify skipping tree iteration when collecting omits' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup r5' '
+       git init r5 &&
+       mkdir -p r5/subdir &&
+
+       echo 1     >r5/short-root          &&
+       echo 12345 >r5/long-root           &&
+       echo a     >r5/subdir/short-subdir &&
+       echo abcde >r5/subdir/long-subdir  &&
+
+       git -C r5 add short-root long-root subdir &&
+       git -C r5 commit -m "commit msg"
+'
+
+test_expect_success 'verify collecting omits in combined: filter' '
+       # Note that this test guards against the naive implementation of simply
+       # giving both filters the same "omits" set and expecting it to
+       # automatically merge them.
+       git -C r5 rev-list --objects --quiet --filter-print-omitted \
+               --filter=combine:tree:2+blob:limit=3 HEAD >actual &&
+
+       # Expect 0 trees/commits, 3 blobs omitted (all blobs except short-root)
+       omitted_1=$(echo 12345 | git hash-object --stdin) &&
+       omitted_2=$(echo a     | git hash-object --stdin) &&
+       omitted_3=$(echo abcde | git hash-object --stdin) &&
+
+       grep ~$omitted_1 actual &&
+       grep ~$omitted_2 actual &&
+       grep ~$omitted_3 actual &&
+       test_line_count = 3 actual
+'
+
 # Test tree:<depth> where a tree is iterated to twice - once where a subentry is
 # too deep to be included, and again where the blob inside it is shallow enough
 # to be included. This makes sure we don't use LOFR_MARK_SEEN incorrectly (we
@@ -441,11 +626,4 @@ test_expect_success 'expand blob limit in protocol' '
        grep "blob:limit=1024" trace
 '
 
-test_expect_success 'expand tree depth limit in protocol' '
-       GIT_TRACE_PACKET="$(pwd)/tree_trace" git -c protocol.version=2 clone \
-               --filter=tree:0k "file://$(pwd)/r2" tree &&
-       ! grep "tree:0k" tree_trace &&
-       grep "tree:0" tree_trace
-'
-
 test_done
index ab69aa176d14bf2d8dd88be1d7cbae0560609be8..9c910ce746733cabaafd34adceeb59cf53dca1f7 100755 (executable)
@@ -526,6 +526,25 @@ test_expect_success 'Check ambiguous head and tag refs II (loose)' '
        test_cmp expected actual
 '
 
+test_expect_success 'create tag without tagger' '
+       git tag -a -m "Broken tag" taggerless &&
+       git tag -f taggerless $(git cat-file tag taggerless |
+               sed -e "/^tagger /d" |
+               git hash-object --stdin -w -t tag)
+'
+
+test_atom refs/tags/taggerless type 'commit'
+test_atom refs/tags/taggerless tag 'taggerless'
+test_atom refs/tags/taggerless tagger ''
+test_atom refs/tags/taggerless taggername ''
+test_atom refs/tags/taggerless taggeremail ''
+test_atom refs/tags/taggerless taggerdate ''
+test_atom refs/tags/taggerless committer ''
+test_atom refs/tags/taggerless committername ''
+test_atom refs/tags/taggerless committeremail ''
+test_atom refs/tags/taggerless committerdate ''
+test_atom refs/tags/taggerless subject 'Broken tag'
+
 test_expect_success 'an unusual tag with an incomplete line' '
 
        git tag -m "bogo" bogo &&
index 033871ee5f35c1a143aec6d2b30c5116bf3f94f5..f30b4849b64ea8bd79a82117333b0f7f225def63 100755 (executable)
@@ -137,7 +137,7 @@ test_expect_success 'do not complain about existing broken links (commit)' '
        some message
        EOF
        commit=$(git hash-object -t commit -w broken-commit) &&
-       git gc 2>stderr &&
+       git gc -q 2>stderr &&
        verbose git cat-file -e $commit &&
        test_must_be_empty stderr
 '
@@ -147,7 +147,7 @@ test_expect_success 'do not complain about existing broken links (tree)' '
        100644 blob 0000000000000000000000000000000000000003    foo
        EOF
        tree=$(git mktree --missing <broken-tree) &&
-       git gc 2>stderr &&
+       git gc -q 2>stderr &&
        git cat-file -e $tree &&
        test_must_be_empty stderr
 '
@@ -162,7 +162,7 @@ test_expect_success 'do not complain about existing broken links (tag)' '
        this is a broken tag
        EOF
        tag=$(git hash-object -t tag -w broken-tag) &&
-       git gc 2>stderr &&
+       git gc -q 2>stderr &&
        git cat-file -e $tag &&
        test_must_be_empty stderr
 '
diff --git a/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh b/t/t7503-pre-commit-and-pre-merge-commit-hooks.sh
new file mode 100755 (executable)
index 0000000..b348545
--- /dev/null
@@ -0,0 +1,281 @@
+#!/bin/sh
+
+test_description='pre-commit and pre-merge-commit hooks'
+
+. ./test-lib.sh
+
+HOOKDIR="$(git rev-parse --git-dir)/hooks"
+PRECOMMIT="$HOOKDIR/pre-commit"
+PREMERGE="$HOOKDIR/pre-merge-commit"
+
+# Prepare sample scripts that write their $0 to actual_hooks
+test_expect_success 'sample script setup' '
+       mkdir -p "$HOOKDIR" &&
+       write_script "$HOOKDIR/success.sample" <<-\EOF &&
+       echo $0 >>actual_hooks
+       exit 0
+       EOF
+       write_script "$HOOKDIR/fail.sample" <<-\EOF &&
+       echo $0 >>actual_hooks
+       exit 1
+       EOF
+       write_script "$HOOKDIR/non-exec.sample" <<-\EOF &&
+       echo $0 >>actual_hooks
+       exit 1
+       EOF
+       chmod -x "$HOOKDIR/non-exec.sample" &&
+       write_script "$HOOKDIR/require-prefix.sample" <<-\EOF &&
+       echo $0 >>actual_hooks
+       test $GIT_PREFIX = "success/"
+       EOF
+       write_script "$HOOKDIR/check-author.sample" <<-\EOF
+       echo $0 >>actual_hooks
+       test "$GIT_AUTHOR_NAME" = "New Author" &&
+       test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
+       EOF
+'
+
+test_expect_success 'root commit' '
+       echo "root" >file &&
+       git add file &&
+       git commit -m "zeroth" &&
+       git checkout -b side &&
+       echo "foo" >foo &&
+       git add foo &&
+       git commit -m "make it non-ff" &&
+       git branch side-orig side &&
+       git checkout master
+'
+
+test_expect_success 'setup conflicting branches' '
+       test_when_finished "git checkout master" &&
+       git checkout -b conflicting-a master &&
+       echo a >conflicting &&
+       git add conflicting &&
+       git commit -m conflicting-a &&
+       git checkout -b conflicting-b master &&
+       echo b >conflicting &&
+       git add conflicting &&
+       git commit -m conflicting-b
+'
+
+test_expect_success 'with no hook' '
+       test_when_finished "rm -f actual_hooks" &&
+       echo "foo" >file &&
+       git add file &&
+       git commit -m "first" &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success 'with no hook (merge)' '
+       test_when_finished "rm -f actual_hooks" &&
+       git branch -f side side-orig &&
+       git checkout side &&
+       git merge -m "merge master" master &&
+       git checkout master &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success '--no-verify with no hook' '
+       test_when_finished "rm -f actual_hooks" &&
+       echo "bar" >file &&
+       git add file &&
+       git commit --no-verify -m "bar" &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success '--no-verify with no hook (merge)' '
+       test_when_finished "rm -f actual_hooks" &&
+       git branch -f side side-orig &&
+       git checkout side &&
+       git merge --no-verify -m "merge master" master &&
+       git checkout master &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success 'with succeeding hook' '
+       test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
+       cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
+       echo "$PRECOMMIT" >expected_hooks &&
+       echo "more" >>file &&
+       git add file &&
+       git commit -m "more" &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_expect_success 'with succeeding hook (merge)' '
+       test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
+       cp "$HOOKDIR/success.sample" "$PREMERGE" &&
+       echo "$PREMERGE" >expected_hooks &&
+       git checkout side &&
+       git merge -m "merge master" master &&
+       git checkout master &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_expect_success 'automatic merge fails; both hooks are available' '
+       test_when_finished "rm -f \"$PREMERGE\" \"$PRECOMMIT\"" &&
+       test_when_finished "rm -f expected_hooks actual_hooks" &&
+       test_when_finished "git checkout master" &&
+       cp "$HOOKDIR/success.sample" "$PREMERGE" &&
+       cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
+
+       git checkout conflicting-a &&
+       test_must_fail git merge -m "merge conflicting-b" conflicting-b &&
+       test_path_is_missing actual_hooks &&
+
+       echo "$PRECOMMIT" >expected_hooks &&
+       echo a+b >conflicting &&
+       git add conflicting &&
+       git commit -m "resolve conflict" &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_expect_success '--no-verify with succeeding hook' '
+       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+       cp "$HOOKDIR/success.sample" "$PRECOMMIT" &&
+       echo "even more" >>file &&
+       git add file &&
+       git commit --no-verify -m "even more" &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success '--no-verify with succeeding hook (merge)' '
+       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
+       cp "$HOOKDIR/success.sample" "$PREMERGE" &&
+       git branch -f side side-orig &&
+       git checkout side &&
+       git merge --no-verify -m "merge master" master &&
+       git checkout master &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success 'with failing hook' '
+       test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
+       cp "$HOOKDIR/fail.sample" "$PRECOMMIT" &&
+       echo "$PRECOMMIT" >expected_hooks &&
+       echo "another" >>file &&
+       git add file &&
+       test_must_fail git commit -m "another" &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_expect_success '--no-verify with failing hook' '
+       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+       cp "$HOOKDIR/fail.sample" "$PRECOMMIT" &&
+       echo "stuff" >>file &&
+       git add file &&
+       git commit --no-verify -m "stuff" &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success 'with failing hook (merge)' '
+       test_when_finished "rm -f \"$PREMERGE\" expected_hooks actual_hooks" &&
+       cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
+       echo "$PREMERGE" >expected_hooks &&
+       git checkout side &&
+       test_must_fail git merge -m "merge master" master &&
+       git checkout master &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_expect_success '--no-verify with failing hook (merge)' '
+       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
+       cp "$HOOKDIR/fail.sample" "$PREMERGE" &&
+       git branch -f side side-orig &&
+       git checkout side &&
+       git merge --no-verify -m "merge master" master &&
+       git checkout master &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success POSIXPERM 'with non-executable hook' '
+       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+       cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" &&
+       echo "content" >>file &&
+       git add file &&
+       git commit -m "content" &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success POSIXPERM '--no-verify with non-executable hook' '
+       test_when_finished "rm -f \"$PRECOMMIT\" actual_hooks" &&
+       cp "$HOOKDIR/non-exec.sample" "$PRECOMMIT" &&
+       echo "more content" >>file &&
+       git add file &&
+       git commit --no-verify -m "more content" &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success POSIXPERM 'with non-executable hook (merge)' '
+       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
+       cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
+       git branch -f side side-orig &&
+       git checkout side &&
+       git merge -m "merge master" master &&
+       git checkout master &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success POSIXPERM '--no-verify with non-executable hook (merge)' '
+       test_when_finished "rm -f \"$PREMERGE\" actual_hooks" &&
+       cp "$HOOKDIR/non-exec.sample" "$PREMERGE" &&
+       git branch -f side side-orig &&
+       git checkout side &&
+       git merge --no-verify -m "merge master" master &&
+       git checkout master &&
+       test_path_is_missing actual_hooks
+'
+
+test_expect_success 'with hook requiring GIT_PREFIX' '
+       test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks success" &&
+       cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" &&
+       echo "$PRECOMMIT" >expected_hooks &&
+       echo "more content" >>file &&
+       git add file &&
+       mkdir success &&
+       (
+               cd success &&
+               git commit -m "hook requires GIT_PREFIX = success/"
+       ) &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_expect_success 'with failing hook requiring GIT_PREFIX' '
+       test_when_finished "rm -rf \"$PRECOMMIT\" expected_hooks actual_hooks fail" &&
+       cp "$HOOKDIR/require-prefix.sample" "$PRECOMMIT" &&
+       echo "$PRECOMMIT" >expected_hooks &&
+       echo "more content" >>file &&
+       git add file &&
+       mkdir fail &&
+       (
+               cd fail &&
+               test_must_fail git commit -m "hook must fail"
+       ) &&
+       git checkout -- file &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_expect_success 'check the author in hook' '
+       test_when_finished "rm -f \"$PRECOMMIT\" expected_hooks actual_hooks" &&
+       cp "$HOOKDIR/check-author.sample" "$PRECOMMIT" &&
+       cat >expected_hooks <<-EOF &&
+       $PRECOMMIT
+       $PRECOMMIT
+       $PRECOMMIT
+       EOF
+       test_must_fail git commit --allow-empty -m "by a.u.thor" &&
+       (
+               GIT_AUTHOR_NAME="New Author" &&
+               GIT_AUTHOR_EMAIL="newauthor@example.com" &&
+               export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
+               git commit --allow-empty -m "by new.author via env" &&
+               git show -s
+       ) &&
+       git commit --author="New Author <newauthor@example.com>" \
+               --allow-empty -m "by new.author via command line" &&
+       git show -s &&
+       test_cmp expected_hooks actual_hooks
+'
+
+test_done
diff --git a/t/t7503-pre-commit-hook.sh b/t/t7503-pre-commit-hook.sh
deleted file mode 100755 (executable)
index 984889b..0000000
+++ /dev/null
@@ -1,139 +0,0 @@
-#!/bin/sh
-
-test_description='pre-commit hook'
-
-. ./test-lib.sh
-
-test_expect_success 'with no hook' '
-
-       echo "foo" > file &&
-       git add file &&
-       git commit -m "first"
-
-'
-
-test_expect_success '--no-verify with no hook' '
-
-       echo "bar" > file &&
-       git add file &&
-       git commit --no-verify -m "bar"
-
-'
-
-# now install hook that always succeeds
-HOOKDIR="$(git rev-parse --git-dir)/hooks"
-HOOK="$HOOKDIR/pre-commit"
-mkdir -p "$HOOKDIR"
-cat > "$HOOK" <<EOF
-#!/bin/sh
-exit 0
-EOF
-chmod +x "$HOOK"
-
-test_expect_success 'with succeeding hook' '
-
-       echo "more" >> file &&
-       git add file &&
-       git commit -m "more"
-
-'
-
-test_expect_success '--no-verify with succeeding hook' '
-
-       echo "even more" >> file &&
-       git add file &&
-       git commit --no-verify -m "even more"
-
-'
-
-# now a hook that fails
-cat > "$HOOK" <<EOF
-#!/bin/sh
-exit 1
-EOF
-
-test_expect_success 'with failing hook' '
-
-       echo "another" >> file &&
-       git add file &&
-       test_must_fail git commit -m "another"
-
-'
-
-test_expect_success '--no-verify with failing hook' '
-
-       echo "stuff" >> file &&
-       git add file &&
-       git commit --no-verify -m "stuff"
-
-'
-
-chmod -x "$HOOK"
-test_expect_success POSIXPERM 'with non-executable hook' '
-
-       echo "content" >> file &&
-       git add file &&
-       git commit -m "content"
-
-'
-
-test_expect_success POSIXPERM '--no-verify with non-executable hook' '
-
-       echo "more content" >> file &&
-       git add file &&
-       git commit --no-verify -m "more content"
-
-'
-chmod +x "$HOOK"
-
-# a hook that checks $GIT_PREFIX and succeeds inside the
-# success/ subdirectory only
-cat > "$HOOK" <<EOF
-#!/bin/sh
-test \$GIT_PREFIX = success/
-EOF
-
-test_expect_success 'with hook requiring GIT_PREFIX' '
-
-       echo "more content" >> file &&
-       git add file &&
-       mkdir success &&
-       (
-               cd success &&
-               git commit -m "hook requires GIT_PREFIX = success/"
-       ) &&
-       rmdir success
-'
-
-test_expect_success 'with failing hook requiring GIT_PREFIX' '
-
-       echo "more content" >> file &&
-       git add file &&
-       mkdir fail &&
-       (
-               cd fail &&
-               test_must_fail git commit -m "hook must fail"
-       ) &&
-       rmdir fail &&
-       git checkout -- file
-'
-
-test_expect_success 'check the author in hook' '
-       write_script "$HOOK" <<-\EOF &&
-       test "$GIT_AUTHOR_NAME" = "New Author" &&
-       test "$GIT_AUTHOR_EMAIL" = "newauthor@example.com"
-       EOF
-       test_must_fail git commit --allow-empty -m "by a.u.thor" &&
-       (
-               GIT_AUTHOR_NAME="New Author" &&
-               GIT_AUTHOR_EMAIL="newauthor@example.com" &&
-               export GIT_AUTHOR_NAME GIT_AUTHOR_EMAIL &&
-               git commit --allow-empty -m "by new.author via env" &&
-               git show -s
-       ) &&
-       git commit --author="New Author <newauthor@example.com>" \
-               --allow-empty -m "by new.author via command line" &&
-       git show -s
-'
-
-test_done
index 75512c340366f3034c58effb62c8796d0b1463a8..5d98d66dbdb1ebc427ad4ddd50c1ce058ada0aaf 100755 (executable)
@@ -1698,6 +1698,69 @@ do
        '
 done
 
+test_expect_success 'git config - section' '
+       test_completion "git config br" <<-\EOF
+       branch.Z
+       browser.Z
+       EOF
+'
+
+test_expect_success 'git config - variable name' '
+       test_completion "git config log.d" <<-\EOF
+       log.date Z
+       log.decorate Z
+       EOF
+'
+
+test_expect_success 'git config - value' '
+       test_completion "git config color.pager " <<-\EOF
+       false Z
+       true Z
+       EOF
+'
+
+test_expect_success 'git -c - section' '
+       test_completion "git -c br" <<-\EOF
+       branch.Z
+       browser.Z
+       EOF
+'
+
+test_expect_success 'git -c - variable name' '
+       test_completion "git -c log.d" <<-\EOF
+       log.date=Z
+       log.decorate=Z
+       EOF
+'
+
+test_expect_success 'git -c - value' '
+       test_completion "git -c color.pager=" <<-\EOF
+       false Z
+       true Z
+       EOF
+'
+
+test_expect_success 'git clone --config= - section' '
+       test_completion "git clone --config=br" <<-\EOF
+       branch.Z
+       browser.Z
+       EOF
+'
+
+test_expect_success 'git clone --config= - variable name' '
+       test_completion "git clone --config=log.d" <<-\EOF
+       log.date=Z
+       log.decorate=Z
+       EOF
+'
+
+test_expect_success 'git clone --config= - value' '
+       test_completion "git clone --config=color.pager=" <<-\EOF
+       false Z
+       true Z
+       EOF
+'
+
 test_expect_success 'sourcing the completion script clears cached commands' '
        __git_compute_all_commands &&
        verbose test -n "$__git_all_commands" &&
diff --git a/templates/hooks--pre-merge-commit.sample b/templates/hooks--pre-merge-commit.sample
new file mode 100755 (executable)
index 0000000..399eab1
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git merge" with no arguments.  The hook should
+# exit with non-zero status after issuing an appropriate message to
+# stderr if it wants to stop the merge commit.
+#
+# To enable this hook, rename this file to "pre-merge-commit".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+        exec "$GIT_DIR/hooks/pre-commit"
+:
diff --git a/trace.c b/trace.c
index fa4a2e7120e405f3cf2d12422802b785f9e37fad..b3ef0e627f8cec18433c17ffb080575894882758 100644 (file)
--- a/trace.c
+++ b/trace.c
@@ -88,8 +88,6 @@ static int prepare_trace_line(const char *file, int line,
        if (!trace_want(key))
                return 0;
 
-       set_try_to_free_routine(NULL);  /* is never reset */
-
        /* unit tests may want to disable additional trace output */
        if (trace_want(&trace_bare))
                return 1;
index 5dda0ca1cdb5d09a8cdfb27dd45d4436f3e8aa6f..c69857515fa5e5c0dcc937fc33b894058e04bcfb 100644 (file)
@@ -215,13 +215,8 @@ static int tr2_dst_try_unix_domain_socket(struct tr2_dst *dst,
 static void tr2_dst_malformed_warning(struct tr2_dst *dst,
                                      const char *tgt_value)
 {
-       struct strbuf buf = STRBUF_INIT;
-
-       strbuf_addf(&buf, "trace2: unknown value for '%s': '%s'",
-                   tr2_sysenv_display_name(dst->sysenv_var), tgt_value);
-       warning("%s", buf.buf);
-
-       strbuf_release(&buf);
+       warning("trace2: unknown value for '%s': '%s'",
+               tr2_sysenv_display_name(dst->sysenv_var), tgt_value);
 }
 
 int tr2_dst_get_trace_fd(struct tr2_dst *dst)
index 6b05a88faf59ee74840b0f9fa340a5d9397a0f5d..9e1279b928bb3eadf110e43c091b4a3ba789fe3b 100644 (file)
@@ -33,6 +33,16 @@ struct helper_data {
                check_connectivity : 1,
                no_disconnect_req : 1,
                no_private_update : 1;
+
+       /*
+        * As an optimization, the transport code may invoke fetch before
+        * get_refs_list. If this happens, and if the transport helper doesn't
+        * support connect or stateless_connect, we need to invoke
+        * get_refs_list ourselves if we haven't already done so. Keep track of
+        * whether we have invoked get_refs_list.
+        */
+       unsigned get_refs_list_called : 1;
+
        char *export_marks;
        char *import_marks;
        /* These go from remote name (as in "list") to private name */
@@ -652,17 +662,25 @@ static int connect_helper(struct transport *transport, const char *name,
        return 0;
 }
 
+static struct ref *get_refs_list_using_list(struct transport *transport,
+                                           int for_push);
+
 static int fetch(struct transport *transport,
                 int nr_heads, struct ref **to_fetch)
 {
        struct helper_data *data = transport->data;
        int i, count;
 
+       get_helper(transport);
+
        if (process_connect(transport, 0)) {
                do_take_over(transport);
                return transport->vtable->fetch(transport, nr_heads, to_fetch);
        }
 
+       if (!data->get_refs_list_called)
+               get_refs_list_using_list(transport, 0);
+
        count = 0;
        for (i = 0; i < nr_heads; i++)
                if (!(to_fetch[i]->status & REF_STATUS_UPTODATE))
@@ -682,13 +700,9 @@ static int fetch(struct transport *transport,
                set_helper_option(transport, "update-shallow", "true");
 
        if (data->transport_options.filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(
-                       &data->transport_options.filter_options,
-                       &expanded_filter_spec);
-               set_helper_option(transport, "filter",
-                                 expanded_filter_spec.buf);
-               strbuf_release(&expanded_filter_spec);
+               const char *spec = expand_list_objects_filter_spec(
+                       &data->transport_options.filter_options);
+               set_helper_option(transport, "filter", spec);
        }
 
        if (data->transport_options.negotiation_tips)
@@ -1058,6 +1072,19 @@ static int has_attribute(const char *attrs, const char *attr)
 
 static struct ref *get_refs_list(struct transport *transport, int for_push,
                                 const struct argv_array *ref_prefixes)
+{
+       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 get_refs_list_using_list(transport, for_push);
+}
+
+static struct ref *get_refs_list_using_list(struct transport *transport,
+                                           int for_push)
 {
        struct helper_data *data = transport->data;
        struct child_process *helper;
@@ -1066,13 +1093,9 @@ static struct ref *get_refs_list(struct transport *transport, int for_push,
        struct ref *posn;
        struct strbuf buf = STRBUF_INIT;
 
+       data->get_refs_list_called = 1;
        helper = get_helper(transport);
 
-       if (process_connect(transport, for_push)) {
-               do_take_over(transport);
-               return transport->vtable->get_refs_list(transport, for_push, ref_prefixes);
-       }
-
        if (data->push && for_push)
                write_str_in_full(helper->in, "list for-push\n");
        else
@@ -1119,7 +1142,6 @@ static struct ref *get_refs_list(struct transport *transport, int for_push,
 }
 
 static struct transport_vtable vtable = {
-       0,
        set_helper_option,
        get_refs_list,
        fetch,
index 004bee5e368f24963d925af997aec051189e1acf..1cde6258a73bcf8582b0746d1c44a23b30115dc9 100644 (file)
@@ -6,12 +6,6 @@ struct transport;
 struct argv_array;
 
 struct transport_vtable {
-       /**
-        * This transport supports the fetch() function being called
-        * without get_refs_list() first being called.
-        */
-       unsigned fetch_without_list : 1;
-
        /**
         * Returns 0 if successful, positive if the option is not
         * recognized or is inapplicable, and negative if the option
index 778c60bf573a714134435e438da5d390fa490484..ae558af94414bfba0592c0915a0cd552438c4f1f 100644 (file)
@@ -122,6 +122,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
 struct bundle_transport_data {
        int fd;
        struct bundle_header header;
+       unsigned get_refs_from_bundle_called : 1;
 };
 
 static struct ref *get_refs_from_bundle(struct transport *transport,
@@ -135,6 +136,8 @@ static struct ref *get_refs_from_bundle(struct transport *transport,
        if (for_push)
                return NULL;
 
+       data->get_refs_from_bundle_called = 1;
+
        if (data->fd > 0)
                close(data->fd);
        data->fd = read_bundle_header(transport->url, &data->header);
@@ -154,6 +157,9 @@ static int fetch_refs_from_bundle(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
        struct bundle_transport_data *data = transport->data;
+
+       if (!data->get_refs_from_bundle_called)
+               get_refs_from_bundle(transport, 0, NULL);
        return unbundle(the_repository, &data->header, data->fd,
                        transport->progress ? BUNDLE_VERBOSE : 0);
 }
@@ -224,6 +230,7 @@ static int set_git_option(struct git_transport_options *opts,
                opts->no_dependents = !!value;
                return 0;
        } else if (!strcmp(name, TRANS_OPT_LIST_OBJECTS_FILTER)) {
+               list_objects_filter_die_if_populated(&opts->filter_options);
                parse_list_objects_filter(&opts->filter_options, value);
                return 0;
        }
@@ -742,7 +749,6 @@ static int disconnect_git(struct transport *transport)
 }
 
 static struct transport_vtable taken_over_vtable = {
-       1,
        NULL,
        get_refs_via_connect,
        fetch_refs_via_pack,
@@ -892,7 +898,6 @@ void transport_check_allowed(const char *type)
 }
 
 static struct transport_vtable bundle_vtable = {
-       0,
        NULL,
        get_refs_from_bundle,
        fetch_refs_from_bundle,
@@ -902,7 +907,6 @@ static struct transport_vtable bundle_vtable = {
 };
 
 static struct transport_vtable builtin_smart_vtable = {
-       1,
        NULL,
        get_refs_via_connect,
        fetch_refs_via_pack,
@@ -1285,15 +1289,6 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
        struct ref **heads = NULL;
        struct ref *rm;
 
-       if (!transport->vtable->fetch_without_list)
-               /*
-                * Some transports (e.g. the built-in bundle transport and the
-                * transport helper interface) do not work when fetching is
-                * done immediately after transport creation. List the remote
-                * refs anyway (if not already listed) as a workaround.
-                */
-               transport_get_remote_refs(transport, NULL);
-
        for (rm = refs; rm; rm = rm->next) {
                nr_refs++;
                if (rm->peer_ref &&
index 50f257bd5c768ae87f8e526c587fa11afad98ad6..9c25126aecaabc8f0e3d0841ee1aa959acdf8dbd 100644 (file)
@@ -16,7 +16,7 @@
 #include "submodule-config.h"
 #include "fsmonitor.h"
 #include "object-store.h"
-#include "fetch-object.h"
+#include "promisor-remote.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -400,7 +400,7 @@ static int check_updates(struct unpack_trees_options *o)
                load_gitmodules_file(index, &state);
 
        enable_delayed_checkout(&state);
-       if (repository_format_partial_clone && o->update && !o->dry_run) {
+       if (has_promisor_remote() && o->update && !o->dry_run) {
                /*
                 * Prefetch the objects that are to be checked out in the loop
                 * below.
@@ -419,8 +419,8 @@ static int check_updates(struct unpack_trees_options *o)
                        oid_array_append(&to_fetch, &ce->oid);
                }
                if (to_fetch.nr)
-                       fetch_objects(repository_format_partial_clone,
-                                     to_fetch.oid, to_fetch.nr);
+                       promisor_remote_get_direct(the_repository,
+                                                  to_fetch.oid, to_fetch.nr);
                oid_array_clear(&to_fetch);
        }
        for (i = 0; i < index->cache_nr; i++) {
index 222cd3ad8960f352ee711915323ec1f36e5e7673..875db9299682c5d9fb593241e1907c3df9e7d597 100644 (file)
@@ -140,18 +140,17 @@ static void create_pack_file(const struct object_array *have_obj,
                argv_array_push(&pack_objects.args, "--delta-base-offset");
        if (use_include_tag)
                argv_array_push(&pack_objects.args, "--include-tag");
-       if (filter_options.filter_spec) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(&filter_options,
-                                               &expanded_filter_spec);
+       if (filter_options.choice) {
+               const char *spec =
+                       expand_list_objects_filter_spec(&filter_options);
                if (pack_objects.use_shell) {
                        struct strbuf buf = STRBUF_INIT;
-                       sq_quote_buf(&buf, expanded_filter_spec.buf);
+                       sq_quote_buf(&buf, spec);
                        argv_array_pushf(&pack_objects.args, "--filter=%s", buf.buf);
                        strbuf_release(&buf);
                } else {
                        argv_array_pushf(&pack_objects.args, "--filter=%s",
-                                        expanded_filter_spec.buf);
+                                        spec);
                }
        }
 
@@ -884,6 +883,7 @@ static void receive_needs(struct packet_reader *reader, struct object_array *wan
                if (skip_prefix(reader->line, "filter ", &arg)) {
                        if (!filter_capability_requested)
                                die("git upload-pack: filtering capability not negotiated");
+                       list_objects_filter_die_if_populated(&filter_options);
                        parse_list_objects_filter(&filter_options, arg);
                        continue;
                }
@@ -1305,6 +1305,7 @@ static void process_args(struct packet_reader *request,
                }
 
                if (allow_filter && skip_prefix(arg, "filter ", &p)) {
+                       list_objects_filter_die_if_populated(&filter_options);
                        parse_list_objects_filter(&filter_options, p);
                        continue;
                }
diff --git a/url.c b/url.c
index 1b8ef78ceab03784ad48f8411b20669e2ea1ea1f..e34e5e751737aeb10a6afecc0a2cdf0ec78fa5fc 100644 (file)
--- a/url.c
+++ b/url.c
@@ -86,6 +86,12 @@ char *url_decode_mem(const char *url, int len)
        return url_decode_internal(&url, len, NULL, &out, 0);
 }
 
+char *url_percent_decode(const char *encoded)
+{
+       struct strbuf out = STRBUF_INIT;
+       return url_decode_internal(&encoded, strlen(encoded), NULL, &out, 0);
+}
+
 char *url_decode_parameter_name(const char **query)
 {
        struct strbuf out = STRBUF_INIT;
diff --git a/url.h b/url.h
index 00b7d58c33e38b2a48e4e2ab557239b1ed0c759d..2a27c3427763b210b123b56c1e6553f8012f00bc 100644 (file)
--- a/url.h
+++ b/url.h
@@ -7,6 +7,14 @@ int is_url(const char *url);
 int is_urlschemechar(int first_flag, int ch);
 char *url_decode(const char *url);
 char *url_decode_mem(const char *url, int len);
+
+/*
+ * Similar to the url_decode_{,mem} methods above, but doesn't assume there
+ * is a scheme followed by a : at the start of the string. Instead, %-sequences
+ * before any : are also parsed.
+ */
+char *url_percent_decode(const char *encoded);
+
 char *url_decode_parameter_name(const char **query);
 char *url_decode_parameter_value(const char **query);
 
index e74a6d402255b0eaf1022863ba30305930d29b6f..86e3244e15ddc8beaf2fc6e6a0b4b0767c87f96c 100644 (file)
@@ -23,6 +23,14 @@ IPATTERN("ada",
         "[a-zA-Z][a-zA-Z0-9_]*"
         "|[-+]?[0-9][0-9#_.aAbBcCdDeEfF]*([eE][+-]?[0-9_]+)?"
         "|=>|\\.\\.|\\*\\*|:=|/=|>=|<=|<<|>>|<>"),
+PATTERNS("dts",
+        "!;\n"
+        /* lines beginning with a word optionally preceded by '&' or the root */
+        "^[ \t]*((/|&?[a-zA-Z_]).*)",
+        /* -- */
+        /* Property names and math operators */
+        "[a-zA-Z0-9,._+?#-]+"
+        "|[-+*/%&^|!~]|>>|<<|&&|\\|\\|"),
 IPATTERN("fortran",
         "!^([C*]|[ \t]*!)\n"
         "!^[ \t]*MODULE[ \t]+PROCEDURE[ \t]\n"
index 1e45ab7b92749b653484f522701f5d86521b03c7..c55d7722d7be5a5933f2b662f685e6536357380e 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -4,12 +4,6 @@
 #include "cache.h"
 #include "config.h"
 
-static void do_nothing(size_t size)
-{
-}
-
-static void (*try_to_free_routine)(size_t size) = do_nothing;
-
 static int memory_limit_check(size_t size, int gentle)
 {
        static size_t limit = 0;
@@ -30,24 +24,11 @@ static int memory_limit_check(size_t size, int gentle)
        return 0;
 }
 
-try_to_free_t set_try_to_free_routine(try_to_free_t routine)
-{
-       try_to_free_t old = try_to_free_routine;
-       if (!routine)
-               routine = do_nothing;
-       try_to_free_routine = routine;
-       return old;
-}
-
 char *xstrdup(const char *str)
 {
        char *ret = strdup(str);
-       if (!ret) {
-               try_to_free_routine(strlen(str) + 1);
-               ret = strdup(str);
-               if (!ret)
-                       die("Out of memory, strdup failed");
-       }
+       if (!ret)
+               die("Out of memory, strdup failed");
        return ret;
 }
 
@@ -61,19 +42,13 @@ static void *do_xmalloc(size_t size, int gentle)
        if (!ret && !size)
                ret = malloc(1);
        if (!ret) {
-               try_to_free_routine(size);
-               ret = malloc(size);
-               if (!ret && !size)
-                       ret = malloc(1);
-               if (!ret) {
-                       if (!gentle)
-                               die("Out of memory, malloc failed (tried to allocate %lu bytes)",
-                                   (unsigned long)size);
-                       else {
-                               error("Out of memory, malloc failed (tried to allocate %lu bytes)",
-                                     (unsigned long)size);
-                               return NULL;
-                       }
+               if (!gentle)
+                       die("Out of memory, malloc failed (tried to allocate %lu bytes)",
+                           (unsigned long)size);
+               else {
+                       error("Out of memory, malloc failed (tried to allocate %lu bytes)",
+                             (unsigned long)size);
+                       return NULL;
                }
        }
 #ifdef XMALLOC_POISON
@@ -138,14 +113,8 @@ void *xrealloc(void *ptr, size_t size)
        ret = realloc(ptr, size);
        if (!ret && !size)
                ret = realloc(ptr, 1);
-       if (!ret) {
-               try_to_free_routine(size);
-               ret = realloc(ptr, size);
-               if (!ret && !size)
-                       ret = realloc(ptr, 1);
-               if (!ret)
-                       die("Out of memory, realloc failed");
-       }
+       if (!ret)
+               die("Out of memory, realloc failed");
        return ret;
 }
 
@@ -160,14 +129,8 @@ void *xcalloc(size_t nmemb, size_t size)
        ret = calloc(nmemb, size);
        if (!ret && (!nmemb || !size))
                ret = calloc(1, 1);
-       if (!ret) {
-               try_to_free_routine(nmemb * size);
-               ret = calloc(nmemb, size);
-               if (!ret && (!nmemb || !size))
-                       ret = calloc(1, 1);
-               if (!ret)
-                       die("Out of memory, calloc failed");
-       }
+       if (!ret)
+               die("Out of memory, calloc failed");
        return ret;
 }