summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.clang-format2
-rw-r--r--.editorconfig16
-rw-r--r--.gitignore3
-rw-r--r--.mailmap2
-rw-r--r--Documentation/CodingGuidelines23
-rw-r--r--Documentation/RelNotes/2.20.0.txt132
-rw-r--r--Documentation/config.txt38
-rw-r--r--Documentation/git-archimport.txt5
-rw-r--r--Documentation/git-column.txt35
-rw-r--r--Documentation/git-commit-graph.txt31
-rw-r--r--Documentation/git-config.txt8
-rw-r--r--Documentation/git-describe.txt4
-rw-r--r--Documentation/git-diff.txt4
-rw-r--r--Documentation/git-gc.txt3
-rw-r--r--Documentation/git-grep.txt11
-rw-r--r--Documentation/git-help.txt12
-rw-r--r--Documentation/git-rebase.txt2
-rw-r--r--Documentation/git.txt6
-rw-r--r--Documentation/gitcredentials.txt6
-rw-r--r--Documentation/howto/update-hook-example.txt2
-rw-r--r--Documentation/technical/api-diff.txt4
-rw-r--r--Documentation/technical/api-history-graph.txt3
-rw-r--r--Documentation/technical/api-revision-walking.txt4
-rw-r--r--Documentation/technical/commit-graph.txt26
-rw-r--r--Documentation/technical/index-format.txt41
-rw-r--r--Makefile51
-rw-r--r--apply.c19
-rw-r--r--archive-zip.c14
-rw-r--r--archive.c2
-rw-r--r--bisect.c4
-rw-r--r--blame.c63
-rw-r--r--builtin/add.c4
-rw-r--r--builtin/am.c12
-rw-r--r--builtin/blame.c4
-rw-r--r--builtin/cat-file.c6
-rw-r--r--builtin/checkout.c10
-rw-r--r--builtin/commit-graph.c20
-rw-r--r--builtin/commit.c38
-rw-r--r--builtin/describe.c4
-rw-r--r--builtin/diff-files.c2
-rw-r--r--builtin/diff-index.c2
-rw-r--r--builtin/diff-tree.c2
-rw-r--r--builtin/diff.c4
-rw-r--r--builtin/difftool.c2
-rw-r--r--builtin/fast-export.c2
-rw-r--r--builtin/fetch.c47
-rw-r--r--builtin/fmt-merge-msg.c2
-rw-r--r--builtin/gc.c56
-rw-r--r--builtin/grep.c9
-rw-r--r--builtin/hash-object.c2
-rw-r--r--builtin/help.c36
-rw-r--r--builtin/log.c25
-rw-r--r--builtin/merge-tree.c2
-rw-r--r--builtin/merge.c16
-rw-r--r--builtin/pack-objects.c70
-rw-r--r--builtin/prune.c2
-rw-r--r--builtin/pull.c9
-rw-r--r--builtin/range-diff.c2
-rw-r--r--builtin/receive-pack.c5
-rw-r--r--builtin/reflog.c2
-rw-r--r--builtin/replace.c10
-rw-r--r--builtin/rerere.c6
-rw-r--r--builtin/reset.c1
-rw-r--r--builtin/rev-list.c2
-rw-r--r--builtin/revert.c2
-rw-r--r--builtin/shortlog.c2
-rw-r--r--builtin/submodule--helper.c2
-rw-r--r--builtin/update-index.c2
-rw-r--r--bundle.c4
-rw-r--r--cache-tree.c8
-rw-r--r--cache.h23
-rw-r--r--combine-diff.c32
-rw-r--r--command-list.txt6
-rw-r--r--commit-graph.c119
-rw-r--r--commit-graph.h6
-rw-r--r--commit-reach.c10
-rw-r--r--commit.c2
-rw-r--r--commit.h1
-rw-r--r--compat/poll/poll.c6
-rw-r--r--compat/poll/poll.h15
-rw-r--r--config.c20
-rw-r--r--config.h1
-rw-r--r--config.mak.uname4
-rw-r--r--connected.h6
-rw-r--r--contrib/completion/git-completion.bash15
-rwxr-xr-xcontrib/coverage-diff.sh108
-rwxr-xr-xcontrib/subtree/git-subtree.sh129
-rw-r--r--delta-islands.h4
-rw-r--r--diff-lib.c21
-rw-r--r--diff-no-index.c9
-rw-r--r--diff.c368
-rw-r--r--diff.h22
-rw-r--r--diffcore-break.c12
-rw-r--r--diffcore-delta.c12
-rw-r--r--diffcore-pickaxe.c8
-rw-r--r--diffcore-rename.c35
-rw-r--r--diffcore.h13
-rw-r--r--ewah/ewok_rlw.h2
-rw-r--r--fetch-pack.c169
-rw-r--r--fetch-pack.h7
-rw-r--r--fuzz-pack-headers.c14
-rw-r--r--fuzz-pack-idx.c13
-rw-r--r--git-compat-util.h4
-rwxr-xr-xgit-rebase.sh6
-rw-r--r--git.c31
-rw-r--r--graph.c58
-rw-r--r--grep.c34
-rw-r--r--grep.h10
-rw-r--r--help.c50
-rw-r--r--http-push.c2
-rw-r--r--json-writer.h2
-rw-r--r--khash.h9
-rw-r--r--line-log.c25
-rw-r--r--line-range.c22
-rw-r--r--line-range.h6
-rw-r--r--list-objects.c8
-rw-r--r--ll-merge.c17
-rw-r--r--ll-merge.h5
-rw-r--r--merge-blobs.c16
-rw-r--r--merge-blobs.h7
-rw-r--r--merge-recursive.c151
-rw-r--r--merge.c20
-rw-r--r--midx.c8
-rw-r--r--midx.h3
-rw-r--r--notes-merge.c9
-rw-r--r--oidset.c41
-rw-r--r--oidset.h43
-rw-r--r--pack-bitmap-write.c2
-rw-r--r--packfile.c44
-rw-r--r--packfile.h13
-rw-r--r--patch-ids.c4
-rw-r--r--patch-ids.h3
-rw-r--r--preload-index.c53
-rw-r--r--read-cache.c821
-rw-r--r--ref-filter.c2
-rw-r--r--refs.c48
-rw-r--r--refs.h12
-rw-r--r--refs/iterator.c6
-rw-r--r--refs/packed-backend.h2
-rw-r--r--refs/ref-cache.h3
-rw-r--r--refs/refs-internal.h11
-rw-r--r--remote.c2
-rw-r--r--replace-object.c7
-rw-r--r--replace-object.h2
-rw-r--r--rerere.c121
-rw-r--r--rerere.h10
-rw-r--r--revision.c76
-rw-r--r--revision.h15
-rw-r--r--sequencer.c24
-rw-r--r--sha1-file.c50
-rw-r--r--shallow.c2
-rw-r--r--split-index.c135
-rw-r--r--submodule.c36
-rw-r--r--submodule.h9
-rw-r--r--t/README87
-rw-r--r--t/helper/test-repository.c10
-rw-r--r--t/helper/test-revision-walking.c2
-rwxr-xr-xt/lib-gpg.sh9
-rwxr-xr-xt/t0000-basic.sh6
-rwxr-xr-xt/t0012-help.sh4
-rwxr-xr-xt/t0014-alias.sh40
-rwxr-xr-xt/t0021-conversion.sh4
-rwxr-xr-xt/t0410-partial-clone.sh41
-rwxr-xr-xt/t1006-cat-file.sh8
-rwxr-xr-xt/t1090-sparse-checkout-scope.sh33
-rwxr-xr-xt/t1300-config.sh9
-rwxr-xr-xt/t1303-wacky-config.sh4
-rwxr-xr-xt/t1400-update-ref.sh2
-rwxr-xr-xt/t1500-rev-parse.sh16
-rwxr-xr-xt/t1700-split-index.sh64
-rwxr-xr-xt/t1701-racy-split-index.sh214
-rwxr-xr-xt/t2101-update-index-reupdate.sh2
-rwxr-xr-xt/t3200-branch.sh2
-rwxr-xr-xt/t3320-notes-merge-worktrees.sh4
-rwxr-xr-xt/t3400-rebase.sh8
-rwxr-xr-xt/t3417-rebase-whitespace-fix.sh6
-rwxr-xr-xt/t3702-add-edit.sh4
-rwxr-xr-xt/t3903-stash.sh8
-rwxr-xr-xt/t3905-stash-include-untracked.sh2
-rwxr-xr-xt/t4025-hunk-header.sh2
-rwxr-xr-xt/t4117-apply-reject.sh6
-rwxr-xr-xt/t4124-apply-ws-rule.sh30
-rwxr-xr-xt/t4138-apply-ws-expansion.sh2
-rwxr-xr-xt/t4214-log-graph-octopus.sh102
-rwxr-xr-xt/t5317-pack-objects-filter-objects.sh360
-rwxr-xr-xt/t5318-commit-graph.sh62
-rwxr-xr-xt/t5410-receive-pack-alternates.sh41
-rwxr-xr-xt/t5500-fetch-pack.sh7
-rwxr-xr-xt/t5551-http-fetch-smart.sh83
-rwxr-xr-xt/t5616-partial-clone.sh67
-rwxr-xr-xt/t5701-git-serve.sh14
-rwxr-xr-xt/t5702-protocol-v2.sh31
-rwxr-xr-xt/t6023-merge-file.sh12
-rwxr-xr-xt/t6027-merge-binary.sh4
-rwxr-xr-xt/t6031-merge-filemode.sh2
-rwxr-xr-xt/t6112-rev-list-filters-objects.sh237
-rwxr-xr-xt/t6500-gc.sh27
-rwxr-xr-xt/t7005-editor.sh2
-rwxr-xr-xt/t7201-co.sh4
-rwxr-xr-xt/t7406-submodule-update.sh8
-rwxr-xr-xt/t7500-commit.sh23
-rwxr-xr-xt/t7501-commit.sh10
-rwxr-xr-xt/t7519-status-fsmonitor.sh11
-rwxr-xr-xt/t7800-difftool.sh2
-rwxr-xr-xt/t7810-grep.sh12
-rwxr-xr-xt/t9100-git-svn-basic.sh2
-rwxr-xr-xt/t9101-git-svn-props.sh34
-rwxr-xr-xt/t9133-git-svn-nested-git-repo.sh6
-rwxr-xr-xt/t9600-cvsimport.sh2
-rwxr-xr-xt/t9603-cvsimport-patchsets.sh4
-rwxr-xr-xt/t9604-cvsimport-timestamps.sh4
-rw-r--r--t/test-lib.sh35
-rw-r--r--transport-helper.c1
-rw-r--r--transport-internal.h6
-rw-r--r--transport.c101
-rw-r--r--transport.h2
-rw-r--r--tree-diff.c2
-rw-r--r--upload-pack.c2
-rw-r--r--userdiff.c5
-rw-r--r--userdiff.h5
-rw-r--r--ws.c5
-rw-r--r--wt-status.c181
-rw-r--r--wt-status.h40
223 files changed, 4659 insertions, 1852 deletions
diff --git a/.clang-format b/.clang-format
index 12a89f95f9..de1c8b5c77 100644
--- a/.clang-format
+++ b/.clang-format
@@ -6,6 +6,8 @@
# Use tabs whenever we need to fill whitespace that spans at least from one tab
# stop to the next one.
+#
+# These settings are mirrored in .editorconfig. Keep them in sync.
UseTab: Always
TabWidth: 8
IndentWidth: 8
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..42cdc4bbfb
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,16 @@
+[*]
+charset = utf-8
+insert_final_newline = true
+
+# The settings for C (*.c and *.h) files are mirrored in .clang-format. Keep
+# them in sync.
+[*.{c,h,sh,perl,pl,pm}]
+indent_style = tab
+tab_width = 8
+
+[*.py]
+indent_style = space
+indent_size = 4
+
+[COMMIT_EDITMSG]
+max_line_length = 72
diff --git a/.gitignore b/.gitignore
index 9d1363a1eb..64b3377d40 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
+/fuzz_corpora
+/fuzz-pack-headers
+/fuzz-pack-idx
/GIT-BUILD-OPTIONS
/GIT-CFLAGS
/GIT-LDFLAGS
diff --git a/.mailmap b/.mailmap
index f165222a78..bef3352b0d 100644
--- a/.mailmap
+++ b/.mailmap
@@ -25,7 +25,7 @@ Ben Walton <bdwalton@gmail.com> <bwalton@artsci.utoronto.ca>
Benoit Sigoure <tsunanet@gmail.com> <tsuna@lrde.epita.fr>
Bernt Hansen <bernt@norang.ca> <bernt@alumni.uwaterloo.ca>
Brandon Casey <drafnel@gmail.com> <casey@nrlssc.navy.mil>
-brian m. carlson <sandals@crustytoothpaste.net> Brian M. Carlson <sandals@crustytoothpaste.ath.cx>
+brian m. carlson <sandals@crustytoothpaste.net>
brian m. carlson <sandals@crustytoothpaste.net> <sandals@crustytoothpaste.ath.cx>
Bryan Larsen <bryan@larsen.st> <bryan.larsen@gmail.com>
Bryan Larsen <bryan@larsen.st> <bryanlarsen@yahoo.com>
diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines
index 48aa4edfbd..8579530710 100644
--- a/Documentation/CodingGuidelines
+++ b/Documentation/CodingGuidelines
@@ -118,6 +118,24 @@ For shell scripts specifically (not exhaustive):
do this
fi
+ - If a command sequence joined with && or || or | spans multiple
+ lines, put each command on a separate line and put && and || and |
+ operators at the end of each line, rather than the start. This
+ means you don't need to use \ to join lines, since the above
+ operators imply the sequence isn't finished.
+
+ (incorrect)
+ grep blob verify_pack_result \
+ | awk -f print_1.awk \
+ | sort >actual &&
+ ...
+
+ (correct)
+ grep blob verify_pack_result |
+ awk -f print_1.awk |
+ sort >actual &&
+ ...
+
- We prefer "test" over "[ ... ]".
- We do not write the noiseword "function" in front of shell
@@ -358,7 +376,10 @@ For C programs:
string_list for sorted string lists, a hash map (mapping struct
objects) named "struct decorate", amongst other things.
- - When you come up with an API, document it.
+ - When you come up with an API, document its functions and structures
+ in the header file that exposes the API to its callers. Use what is
+ in "strbuf.h" as a model for the appropriate tone and level of
+ detail.
- The first #include in C files, except in platform specific compat/
implementations, must be either "git-compat-util.h", "cache.h" or
diff --git a/Documentation/RelNotes/2.20.0.txt b/Documentation/RelNotes/2.20.0.txt
index 2cc5fd7a9f..ab631660f9 100644
--- a/Documentation/RelNotes/2.20.0.txt
+++ b/Documentation/RelNotes/2.20.0.txt
@@ -14,6 +14,9 @@ Backward Compatibility Notes
which means some fetches of tags that did not fail with older
version of Git will fail without "--force" with this version.
+ * "git help -a" now gives verbose output (same as "git help -av").
+ Those who want the old output may say "git help --no-verbose -a"..
+
Updates since v2.19
-------------------
@@ -58,6 +61,46 @@ UI, Workflows & Features
* "git multi-pack-index" learned to detect corruption in the .midx
file it uses, and this feature has been integrated into "git fsck".
+ * Generation of (experimental) commit-graph files have so far been
+ fairly silent, even though it takes noticeable amount of time in a
+ meaningfully large repository. The users will now see progress
+ output.
+
+ * The minimum version of Windows supported by Windows port fo Git is
+ now set to Vista.
+
+ * The completion script (in contrib/) learned to complete a handful of
+ options "git stash list" command takes.
+
+ * The completion script (in contrib/) learned that "git fetch
+ --multiple" only takes remote names as arguments and no refspecs.
+
+ * "git status" learns to show progress bar when refreshing the index
+ takes a long time.
+ (merge ae9af12287 nd/status-refresh-progress later to maint).
+
+ * "git help -a" and "git help -av" give different pieces of
+ information, and generally the "verbose" version is more friendly
+ to the new users. "git help -a" by default now uses the more
+ verbose output (with "--no-verbose", you can go back to the
+ original). Also "git help -av" now lists aliases and external
+ commands, which it did not used to.
+
+ * Unlike "grep", "git grep" by default recurses to the whole tree.
+ The command learned "git grep --recursive" option, so that "git
+ grep --no-recursive" can serve as a synonym to setting the
+ max-depth to 0.
+
+ * When pushing into a repository that borrows its objects from an
+ alternate object store, "git receive-pack" that responds to the
+ push request on the other side lists the tips of refs in the
+ alternate to reduce the amount of objects transferred. This
+ sometimes is detrimental when the number of refs in the alternate
+ is absurdly large, in which case the bandwidth saved in potentially
+ fewer objects transferred is wasted in excessively large ref
+ advertisement. The alternate refs that are advertised are now
+ configurable with a pair of configuration variables.
+
Performance, Internal Implementation, Development Support etc.
@@ -108,6 +151,51 @@ Performance, Internal Implementation, Development Support etc.
* Update fsck.skipList implementation and documentation.
(merge 371a655074 ab/fsck-skiplist later to maint).
+ * An alias that expands to another alias has so far been forbidden,
+ but now it is allowed to create such an alias.
+
+ * Various test scripts have been updated for style and also correct
+ handling of exit status of various commands.
+
+ * "gc --auto" ended up calling exit(-1) upon error, which has been
+ corrected to use exit(1). Also the error reporting behaviour when
+ daemonized has been updated to exit with zero status when stopping
+ due to a previously discovered error (which implies there is no
+ point running gc to improve the situation); we used to exit with
+ failure in such a case.
+
+ * Various codepaths in the core-ish part learned to work on an
+ arbitrary in-core index structure, not necessarily the default
+ instance "the_index".
+ (merge b3c7eef9b0 nd/the-index later to maint).
+
+ * Code clean-up in the internal machinery used by "git status" and
+ "git commit --dry-run".
+ (merge 73ba5d78b4 ss/wt-status-committable later to maint).
+
+ * Some environment variables that control the runtime options of Git
+ used during tests are getting renamed for consistency.
+ (merge 4231d1ba99 bp/rename-test-env-var later to maint).
+
+ * A new extension to the index file has been introduced, which allows
+ the index file to be read in parallel for performance.
+
+ * The oidset API was built on top of the oidmap API which in turn is
+ on the hashmap API. Replace the implementation to build on top of
+ the khash API and gain performance.
+
+ * Over some transports, fetching objects with an exact commit object
+ name can be done without first seeing the ref advertisements. The
+ code has been optimized to exploit this.
+
+ * In a partial clone that will lazily be hydrated from the
+ originating repository, we generally want to avoid "does this
+ object exist (locally)?" on objects that we deliberately omitted
+ when we created the clone. The cache-tree codepath (which is used
+ to write a tree object out of the index) however insisted that the
+ object exists, even for paths that are outside of the partial
+ checkout area. The code has been updated to avoid such a check.
+
Fixes since v2.19
-----------------
@@ -170,6 +258,38 @@ Fixes since v2.19
segfault, which has been corrected.
(merge e467a90c7a tg/range-diff-corner-case-fix later to maint).
+ * The recently introduced commit-graph auxiliary data is incompatible
+ with mechanisms such as replace & grafts that "breaks" immutable
+ nature of the object reference relationship. Disable optimizations
+ based on its use (and updating existing commit-graph) when these
+ incompatible features are in use in the repository.
+ (merge 829a321569 ds/commit-graph-with-grafts later to maint).
+
+ * The mailmap file update.
+ (merge 255eb03edf jn/mailmap-update later to maint).
+
+ * The code in "git status" sometimes hit an assertion failure. This
+ was caused by a structure that was reused without cleaning the data
+ used for the first run, which has been corrected.
+ (merge 3e73cc62c0 en/status-multiple-renames-to-the-same-target-fix later to maint).
+
+ * "git fetch $repo $object" in a partial clone did not correctly
+ fetch the asked-for object that is referenced by an object in
+ promisor packfile, which has been fixed.
+
+ * A corner-case bugfix.
+ (merge c5cbb27cb5 sm/show-superproject-while-conflicted later to maint).
+
+ * Various fixes to "diff --color-moved-ws".
+
+ * A partial clone that is configured to lazily fetch missing objects
+ will on-demand issue a "git fetch" request to the originating
+ repository to fill not-yet-obtained objects. The request has been
+ optimized for requesting a tree object (and not the leaf blob
+ objects contained in it) by telling the originating repository that
+ no blobs are needed.
+ (merge 4c7f9567ea jt/non-blob-lazy-fetch later to maint).
+
* Code cleanup, docfix, build fix, etc.
(merge 96a7501aad ts/doc-build-manpage-xsl-quietly later to maint).
(merge b9b07efdb2 tg/conflict-marker-size later to maint).
@@ -181,3 +301,15 @@ Fixes since v2.19
(merge 0597dd62ba sb/string-list-remove-unused later to maint).
(merge db2d36fad8 bw/protocol-v2 later to maint).
(merge 456d7cd3a9 sg/split-index-test later to maint).
+ (merge 7b6057c852 tq/refs-internal-comment-fix later to maint).
+ (merge 29e8dc50ad tg/t5551-with-curl-7.61.1 later to maint).
+ (merge 55f6bce2c9 fe/doc-updates later to maint).
+ (merge 7987d2232d jk/check-everything-connected-is-long-gone later to maint).
+ (merge 4ba3c9be47 dz/credential-doc-url-matching-rules later to maint).
+ (merge 4c399442f7 ma/commit-graph-docs later to maint).
+ (merge fc0503b04e ma/t1400-undebug-test later to maint).
+ (merge e56b53553a nd/packobjectshook-doc-fix later to maint).
+ (merge c56170a0c4 ma/mailing-list-address-in-git-help later to maint).
+ (merge 6e8fc70fce rs/sequencer-oidset-insert-avoids-dups later to maint).
+ (merge ad0b8f9575 mw/doc-typofixes later to maint).
+ (merge d9f079ad1a jc/how-to-document-api later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 1546833213..552827935a 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -225,7 +225,7 @@ boolean::
false;; Boolean false literals are `no`, `off`, `false`,
`0` and the empty string.
+
-When converting value to the canonical form using `--bool` type
+When converting a value to its canonical form using the `--type=bool` type
specifier, 'git config' will ensure that the output is "true" or
"false" (spelled in lowercase).
@@ -616,6 +616,24 @@ core.preferSymlinkRefs::
This is sometimes needed to work with old scripts that
expect HEAD to be a symbolic link.
+core.alternateRefsCommand::
+ When advertising tips of available history from an alternate, use the shell to
+ execute the specified command instead of linkgit:git-for-each-ref[1]. The
+ first argument is the absolute path of the alternate. Output must contain one
+ hex object id per line (i.e., the same as produce by `git for-each-ref
+ --format='%(objectname)'`).
++
+Note that you cannot generally put `git for-each-ref` directly into the config
+value, as it does not take a repository path as an argument (but you can wrap
+the command above in a shell script).
+
+core.alternateRefsPrefixes::
+ When listing references from an alternate, list only references that begin
+ with the given prefix. Prefixes match as if they were given as arguments to
+ linkgit:git-for-each-ref[1]. To list multiple prefixes, separate them with
+ whitespace. If `core.alternateRefsCommand` is set, setting
+ `core.alternateRefsPrefixes` has no effect.
+
core.bare::
If true this repository is assumed to be 'bare' and has no
working directory associated with it. If this is the case a
@@ -1643,7 +1661,8 @@ gc.writeCommitGraph::
for details.
gc.logExpiry::
- If the file gc.log exists, then `git gc --auto` won't run
+ If the file gc.log exists, then `git gc --auto` will print
+ its content and exit with status zero instead of running
unless that file is more than 'gc.logExpiry' old. Default is
"1.day". See `gc.pruneExpire` for more ways to specify its
value.
@@ -2148,6 +2167,13 @@ imap::
The configuration variables in the 'imap' section are described
in linkgit:git-imap-send[1].
+index.threads::
+ Specifies the number of threads to spawn when loading the index.
+ This is meant to reduce index load time on multiprocessor machines.
+ Specifying 0 or 'true' will cause Git to auto-detect the number of
+ CPU's and set the number of threads accordingly. Specifying 1 or
+ 'false' will disable multithreading. Defaults to 'true'.
+
index.version::
Specify the version with which new index files should be
initialized. This does not affect existing repositories.
@@ -2995,15 +3021,15 @@ uploadpack.packObjectsHook::
was run. I.e., `upload-pack` will feed input intended for
`pack-objects` to the hook, and expects a completed packfile on
stdout.
-
-uploadpack.allowFilter::
- If this option is set, `upload-pack` will support partial
- clone and partial fetch object filtering.
+
Note that this configuration variable is ignored if it is seen in the
repository-level config (this is a safety measure against fetching from
untrusted repositories).
+uploadpack.allowFilter::
+ If this option is set, `upload-pack` will support partial
+ clone and partial fetch object filtering.
+
uploadpack.allowRefInWant::
If this option is set, `upload-pack` will support the `ref-in-want`
feature of the protocol version 2 `fetch` command. This feature
diff --git a/Documentation/git-archimport.txt b/Documentation/git-archimport.txt
index ea70653369..a595a0ffee 100644
--- a/Documentation/git-archimport.txt
+++ b/Documentation/git-archimport.txt
@@ -3,7 +3,7 @@ git-archimport(1)
NAME
----
-git-archimport - Import an Arch repository into Git
+git-archimport - Import a GNU Arch repository into Git
SYNOPSIS
@@ -14,7 +14,8 @@ SYNOPSIS
DESCRIPTION
-----------
-Imports a project from one or more Arch repositories. It will follow branches
+Imports a project from one or more GNU Arch repositories.
+It will follow branches
and repositories within the namespaces defined by the <archive/branch>
parameters supplied. If it cannot find the remote branch a merge comes from
it will just import it as a regular commit. If it can find it, it will mark it
diff --git a/Documentation/git-column.txt b/Documentation/git-column.txt
index 03d18465d4..763afabb6d 100644
--- a/Documentation/git-column.txt
+++ b/Documentation/git-column.txt
@@ -13,7 +13,10 @@ SYNOPSIS
DESCRIPTION
-----------
-This command formats its input into multiple columns.
+This command formats the lines of its standard input into a table with
+multiple columns. Each input line occupies one cell of the table. It
+is used internally by other git commands to format output into
+columns.
OPTIONS
-------
@@ -23,7 +26,7 @@ OPTIONS
--mode=<mode>::
Specify layout mode. See configuration variable column.ui for option
- syntax.
+ syntax in linkgit:git-config[1].
--raw-mode=<n>::
Same as --mode but take mode encoded as a number. This is mainly used
@@ -43,6 +46,34 @@ OPTIONS
--padding=<N>::
The number of spaces between columns. One space by default.
+EXAMPLES
+------
+
+Format data by columns:
+------------
+$ seq 1 24 | git column --mode=column --padding=5
+1 4 7 10 13 16 19 22
+2 5 8 11 14 17 20 23
+3 6 9 12 15 18 21 24
+------------
+
+Format data by rows:
+------------
+$ seq 1 21 | git column --mode=row --padding=5
+1 2 3 4 5 6 7
+8 9 10 11 12 13 14
+15 16 17 18 19 20 21
+------------
+
+List some tags in a table with unequal column widths:
+------------
+$ git tag --list 'v2.4.*' --column=row,dense
+v2.4.0 v2.4.0-rc0 v2.4.0-rc1 v2.4.0-rc2 v2.4.0-rc3
+v2.4.1 v2.4.10 v2.4.11 v2.4.12 v2.4.2
+v2.4.3 v2.4.4 v2.4.5 v2.4.6 v2.4.7
+v2.4.8 v2.4.9
+------------
+
GIT
---
Part of the linkgit:git[1] suite
diff --git a/Documentation/git-commit-graph.txt b/Documentation/git-commit-graph.txt
index dececb79d7..624470e198 100644
--- a/Documentation/git-commit-graph.txt
+++ b/Documentation/git-commit-graph.txt
@@ -3,7 +3,7 @@ git-commit-graph(1)
NAME
----
-git-commit-graph - Write and verify Git commit graph files
+git-commit-graph - Write and verify Git commit-graph files
SYNOPSIS
@@ -17,24 +17,24 @@ SYNOPSIS
DESCRIPTION
-----------
-Manage the serialized commit graph file.
+Manage the serialized commit-graph file.
OPTIONS
-------
--object-dir::
- Use given directory for the location of packfiles and commit graph
+ Use given directory for the location of packfiles and commit-graph
file. This parameter exists to specify the location of an alternate
- that only has the objects directory, not a full .git directory. The
- commit graph file is expected to be at <dir>/info/commit-graph and
- the packfiles are expected to be in <dir>/pack.
+ that only has the objects directory, not a full `.git` directory. The
+ commit-graph file is expected to be at `<dir>/info/commit-graph` and
+ the packfiles are expected to be in `<dir>/pack`.
COMMANDS
--------
'write'::
-Write a commit graph file based on the commits found in packfiles.
+Write a commit-graph file based on the commits found in packfiles.
+
With the `--stdin-packs` option, generate the new commit graph by
walking objects only in the specified pack-indexes. (Cannot be combined
@@ -54,8 +54,8 @@ existing commit-graph file.
'read'::
-Read a graph file given by the commit-graph file and output basic
-details about the graph file. Used for debugging purposes.
+Read the commit-graph file and output basic details about it.
+Used for debugging purposes.
'verify'::
@@ -66,27 +66,28 @@ database. Used to check for corrupted data.
EXAMPLES
--------
-* Write a commit graph file for the packed commits in your local .git folder.
+* Write a commit-graph file for the packed commits in your local `.git`
+ directory.
+
------------------------------------------------
$ git commit-graph write
------------------------------------------------
-* Write a graph file, extending the current graph file using commits
-* in <pack-index>.
+* Write a commit-graph file, extending the current commit-graph file
+ using commits in `<pack-index>`.
+
------------------------------------------------
$ echo <pack-index> | git commit-graph write --stdin-packs
------------------------------------------------
-* Write a graph file containing all reachable commits.
+* Write a commit-graph file containing all reachable commits.
+
------------------------------------------------
$ git show-ref -s | git commit-graph write --stdin-commits
------------------------------------------------
-* Write a graph file containing all commits in the current
-* commit-graph file along with those reachable from HEAD.
+* Write a commit-graph file containing all commits in the current
+ commit-graph file along with those reachable from `HEAD`.
+
------------------------------------------------
$ git rev-parse HEAD | git commit-graph write --stdin-commits --append
diff --git a/Documentation/git-config.txt b/Documentation/git-config.txt
index 8e240435be..5e87d82933 100644
--- a/Documentation/git-config.txt
+++ b/Documentation/git-config.txt
@@ -188,8 +188,8 @@ Valid `<type>`'s include:
--bool-or-int::
--path::
--expiry-date::
- Historical options for selecting a type specifier. Prefer instead `--type`,
- (see: above).
+ Historical options for selecting a type specifier. Prefer instead `--type`
+ (see above).
--no-type::
Un-sets the previously set type specifier (if one was previously set). This
@@ -442,9 +442,9 @@ For URLs in `https://weak.example.com`, `http.sslVerify` is set to
false, while it is set to `true` for all others:
------------
-% git config --bool --get-urlmatch http.sslverify https://good.example.com
+% git config --type=bool --get-urlmatch http.sslverify https://good.example.com
true
-% git config --bool --get-urlmatch http.sslverify https://weak.example.com
+% git config --type=bool --get-urlmatch http.sslverify https://weak.example.com
false
% git config --get-urlmatch http https://weak.example.com
http.cookieFile /tmp/cookie.txt
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index e027fb8c4b..ccdc5f83d6 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -18,7 +18,9 @@ The command finds the most recent tag that is reachable from a
commit. If the tag points to the commit, then only the tag is
shown. Otherwise, it suffixes the tag name with the number of
additional commits on top of the tagged object and the
-abbreviated object name of the most recent commit.
+abbreviated object name of the most recent commit. The result
+is a "human-readable" object name which can also be used to
+identify the commit to other git commands.
By default (without --all or --tags) `git describe` only shows
annotated tags. For more information about creating annotated tags
diff --git a/Documentation/git-diff.txt b/Documentation/git-diff.txt
index b180f1fa5b..030f162f30 100644
--- a/Documentation/git-diff.txt
+++ b/Documentation/git-diff.txt
@@ -72,10 +72,10 @@ two blob objects, or changes between two files on disk.
This form is to view the changes on the branch containing
and up to the second <commit>, starting at a common ancestor
of both <commit>. "git diff A\...B" is equivalent to
- "git diff $(git-merge-base A B) B". You can omit any one
+ "git diff $(git merge-base A B) B". You can omit any one
of <commit>, which has the same effect as using HEAD instead.
-Just in case if you are doing something exotic, it should be
+Just in case you are doing something exotic, it should be
noted that all of the <commit> in the above description, except
in the last two forms that use ".." notations, can be any
<tree>.
diff --git a/Documentation/git-gc.txt b/Documentation/git-gc.txt
index f5bc98ccb3..c20ee6c789 100644
--- a/Documentation/git-gc.txt
+++ b/Documentation/git-gc.txt
@@ -17,7 +17,8 @@ Runs a number of housekeeping tasks within the current repository,
such as compressing file revisions (to reduce disk space and increase
performance), removing unreachable objects which may have been
created from prior invocations of 'git add', packing refs, pruning
-reflog, rerere metadata or stale working trees.
+reflog, rerere metadata or stale working trees. May also update ancillary
+indexes such as the commit-graph.
Users are encouraged to run this task on a regular basis within
each repository to maintain good disk space utilization and good
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index a3049af1a3..84fe236a8e 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -18,7 +18,7 @@ SYNOPSIS
[(-O | --open-files-in-pager) [<pager>]]
[-z | --null]
[ -o | --only-matching ] [-c | --count] [--all-match] [-q | --quiet]
- [--max-depth <depth>]
+ [--max-depth <depth>] [--[no-]recursive]
[--color[=<when>] | --no-color]
[--break] [--heading] [-p | --show-function]
[-A <post-context>] [-B <pre-context>] [-C <context>]
@@ -119,11 +119,18 @@ OPTIONS
--max-depth <depth>::
For each <pathspec> given on command line, descend at most <depth>
- levels of directories. A negative value means no limit.
+ levels of directories. A value of -1 means no limit.
This option is ignored if <pathspec> contains active wildcards.
In other words if "a*" matches a directory named "a*",
"*" is matched literally so --max-depth is still effective.
+-r::
+--recursive::
+ Same as `--max-depth=-1`; this is the default.
+
+--no-recursive::
+ Same as `--max-depth=0`.
+
-w::
--word-regexp::
Match the pattern only at word boundary (either begin at the
diff --git a/Documentation/git-help.txt b/Documentation/git-help.txt
index 83d25d825a..aab5453bbb 100644
--- a/Documentation/git-help.txt
+++ b/Documentation/git-help.txt
@@ -8,7 +8,7 @@ git-help - Display help information about Git
SYNOPSIS
--------
[verse]
-'git help' [-a|--all [--verbose]] [-g|--guide]
+'git help' [-a|--all [--[no-]verbose]] [-g|--guide]
[-i|--info|-m|--man|-w|--web] [COMMAND|GUIDE]
DESCRIPTION
@@ -29,6 +29,10 @@ guide is brought up. The 'man' program is used by default for this
purpose, but this can be overridden by other options or configuration
variables.
+If an alias is given, git shows the definition of the alias on
+standard output. To get the manual page for the aliased command, use
+`git COMMAND --help`.
+
Note that `git --help ...` is identical to `git help ...` because the
former is internally converted into the latter.
@@ -42,8 +46,10 @@ OPTIONS
--all::
Prints all the available commands on the standard output. This
option overrides any given command or guide name.
- When used with `--verbose` print description for all recognized
- commands.
+
+--verbose::
+ When used with `--all` print description for all recognized
+ commands. This is the default.
-c::
--config::
diff --git a/Documentation/git-rebase.txt b/Documentation/git-rebase.txt
index 1fbc6ebcde..432baabe33 100644
--- a/Documentation/git-rebase.txt
+++ b/Documentation/git-rebase.txt
@@ -954,7 +954,7 @@ command fails, it is rescheduled immediately, with a helpful message how
to proceed.
The `reset` command resets the HEAD, index and worktree to the specified
-revision. It is isimilar to an `exec git reset --hard <label>`, but
+revision. It is similar to an `exec git reset --hard <label>`, but
refuses to overwrite untracked files. If the `reset` command fails, it is
rescheduled immediately, with a helpful message how to edit the todo list
(this typically happens when a `reset` command was inserted into the todo
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 74a9d7edb4..2ac9b1c7fe 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -76,7 +76,7 @@ Note that omitting the `=` in `git -c foo.bar ...` is allowed and sets
`foo.bar` to the boolean true value (just like `[foo]bar` would in a
config file). Including the equals but with an empty value (like `git -c
foo.bar= ...`) sets `foo.bar` to the empty string which `git config
---bool` will convert to `false`.
+--type=bool` will convert to `false`.
--exec-path[=<path>]::
Path to wherever your core Git programs are installed.
@@ -858,7 +858,9 @@ Reporting Bugs
Report bugs to the Git mailing list <git@vger.kernel.org> where the
development and maintenance is primarily done. You do not have to be
-subscribed to the list to send a message there.
+subscribed to the list to send a message there. See the list archive
+at https://public-inbox.org/git for previous bug reports and other
+discussions.
Issues which are security relevant should be disclosed privately to
the Git Security mailing list <git-security@googlegroups.com>.
diff --git a/Documentation/gitcredentials.txt b/Documentation/gitcredentials.txt
index f970196bc1..adc759612d 100644
--- a/Documentation/gitcredentials.txt
+++ b/Documentation/gitcredentials.txt
@@ -133,6 +133,12 @@ compares hostnames exactly, without considering whether two hosts are part of
the same domain. Likewise, a config entry for `http://example.com` would not
match: Git compares the protocols exactly.
+If the "pattern" URL does include a path component, then this too must match
+exactly: the context `https://example.com/bar/baz.git` will match a config
+entry for `https://example.com/bar/baz.git` (in addition to matching the config
+entry for `https://example.com`) but will not match a config entry for
+`https://example.com/bar`.
+
CONFIGURATION OPTIONS
---------------------
diff --git a/Documentation/howto/update-hook-example.txt b/Documentation/howto/update-hook-example.txt
index a5193b1e5c..89821ec74f 100644
--- a/Documentation/howto/update-hook-example.txt
+++ b/Documentation/howto/update-hook-example.txt
@@ -80,7 +80,7 @@ case "$1" in
info "The branch '$1' is new..."
else
# updating -- make sure it is a fast-forward
- mb=$(git-merge-base "$2" "$3")
+ mb=$(git merge-base "$2" "$3")
case "$mb,$2" in
"$2,$mb") info "Update is fast-forward" ;;
*) noff=y; info "This is not a fast-forward update.";;
diff --git a/Documentation/technical/api-diff.txt b/Documentation/technical/api-diff.txt
index 8b001de0db..30fc0e9c93 100644
--- a/Documentation/technical/api-diff.txt
+++ b/Documentation/technical/api-diff.txt
@@ -18,8 +18,8 @@ Calling sequence
----------------
* Prepare `struct diff_options` to record the set of diff options, and
- then call `diff_setup()` to initialize this structure. This sets up
- the vanilla default.
+ then call `repo_diff_setup()` to initialize this structure. This
+ sets up the vanilla default.
* Fill in the options structure to specify desired output format, rename
detection, etc. `diff_opt_parse()` can be used to parse options given
diff --git a/Documentation/technical/api-history-graph.txt b/Documentation/technical/api-history-graph.txt
index 18142b6d29..d0d1707c8c 100644
--- a/Documentation/technical/api-history-graph.txt
+++ b/Documentation/technical/api-history-graph.txt
@@ -80,7 +80,7 @@ Calling sequence
it is invoked.
* For each commit, call `graph_next_line()` repeatedly, until
- `graph_is_commit_finished()` returns non-zero. Each call go
+ `graph_is_commit_finished()` returns non-zero. Each call to
`graph_next_line()` will output a single line of the graph. The resulting
lines will not contain any newlines. `graph_next_line()` returns 1 if the
resulting line contains the current commit, or 0 if this is merely a line
@@ -115,7 +115,6 @@ struct commit *commit;
struct git_graph *graph = graph_init(opts);
while ((commit = get_revision(opts)) != NULL) {
- graph_update(graph, commit);
while (!graph_is_commit_finished(graph))
{
struct strbuf sb;
diff --git a/Documentation/technical/api-revision-walking.txt b/Documentation/technical/api-revision-walking.txt
index 55b878ade8..03f9ea6ac4 100644
--- a/Documentation/technical/api-revision-walking.txt
+++ b/Documentation/technical/api-revision-walking.txt
@@ -15,9 +15,9 @@ revision list.
Functions
---------
-`init_revisions`::
+`repo_init_revisions`::
- Initialize a rev_info structure with default values. The second
+ Initialize a rev_info structure with default values. The third
parameter may be NULL or can be prefix path, and then the `.prefix`
variable will be set to it. This is typically the first function you
want to call when you want to deal with a revision list. After calling
diff --git a/Documentation/technical/commit-graph.txt b/Documentation/technical/commit-graph.txt
index c664acbd76..7805b0968c 100644
--- a/Documentation/technical/commit-graph.txt
+++ b/Documentation/technical/commit-graph.txt
@@ -15,13 +15,13 @@ There are two main costs here:
1. Decompressing and parsing commits.
2. Walking the entire graph to satisfy topological order constraints.
-The commit graph file is a supplemental data structure that accelerates
+The commit-graph file is a supplemental data structure that accelerates
commit graph walks. If a user downgrades or disables the 'core.commitGraph'
config setting, then the existing ODB is sufficient. The file is stored
as "commit-graph" either in the .git/objects/info directory or in the info
directory of an alternate.
-The commit graph file stores the commit graph structure along with some
+The commit-graph file stores the commit graph structure along with some
extra metadata to speed up graph walks. By listing commit OIDs in lexi-
cographic order, we can identify an integer position for each commit and
refer to the parents of a commit using those integer positions. We use
@@ -103,7 +103,7 @@ that of a parent.
Design Details
--------------
-- The commit graph file is stored in a file named 'commit-graph' in the
+- The commit-graph file is stored in a file named 'commit-graph' in the
.git/objects/info directory. This could be stored in the info directory
of an alternate.
@@ -112,12 +112,24 @@ Design Details
- The file format includes parameters for the object ID hash function,
so a future change of hash algorithm does not require a change in format.
+- Commit grafts and replace objects can change the shape of the commit
+ history. The latter can also be enabled/disabled on the fly using
+ `--no-replace-objects`. This leads to difficultly storing both possible
+ interpretations of a commit id, especially when computing generation
+ numbers. The commit-graph will not be read or written when
+ replace-objects or grafts are present.
+
+- Shallow clones create grafts of commits by dropping their parents. This
+ leads the commit-graph to think those commits have generation number 1.
+ If and when those commits are made unshallow, those generation numbers
+ become invalid. Since shallow clones are intended to restrict the commit
+ history to a very small set of commits, the commit-graph feature is less
+ helpful for these clones, anyway. The commit-graph will not be read or
+ written when shallow commits are present.
+
Future Work
-----------
-- The commit graph feature currently does not honor commit grafts. This can
- be remedied by duplicating or refactoring the current graft logic.
-
- After computing and storing generation numbers, we must make graph
walks aware of generation numbers to gain the performance benefits they
enable. This will mostly be accomplished by swapping a commit-date-ordered
@@ -127,7 +139,7 @@ Future Work
- 'log --topo-order'
- 'tag --merged'
-- A server could provide a commit graph file as part of the network protocol
+- A server could provide a commit-graph file as part of the network protocol
to avoid extra calculations by clients. This feature is only of benefit if
the user is willing to trust the file, because verifying the file is correct
is as hard as computing it from scratch.
diff --git a/Documentation/technical/index-format.txt b/Documentation/technical/index-format.txt
index db3572626b..7c4d67aa6a 100644
--- a/Documentation/technical/index-format.txt
+++ b/Documentation/technical/index-format.txt
@@ -314,3 +314,44 @@ The remaining data of each directory block is grouped by type:
- An ewah bitmap, the n-th bit indicates whether the n-th index entry
is not CE_FSMONITOR_VALID.
+
+== End of Index Entry
+
+ The End of Index Entry (EOIE) is used to locate the end of the variable
+ length index entries and the begining of the extensions. Code can take
+ advantage of this to quickly locate the index extensions without having
+ to parse through all of the index entries.
+
+ Because it must be able to be loaded before the variable length cache
+ entries and other index extensions, this extension must be written last.
+ The signature for this extension is { 'E', 'O', 'I', 'E' }.
+
+ The extension consists of:
+
+ - 32-bit offset to the end of the index entries
+
+ - 160-bit SHA-1 over the extension types and their sizes (but not
+ their contents). E.g. if we have "TREE" extension that is N-bytes
+ long, "REUC" extension that is M-bytes long, followed by "EOIE",
+ then the hash would be:
+
+ SHA-1("TREE" + <binary representation of N> +
+ "REUC" + <binary representation of M>)
+
+== Index Entry Offset Table
+
+ The Index Entry Offset Table (IEOT) is used to help address the CPU
+ cost of loading the index by enabling multi-threading the process of
+ converting cache entries from the on-disk format to the in-memory format.
+ The signature for this extension is { 'I', 'E', 'O', 'T' }.
+
+ The extension consists of:
+
+ - 32-bit version (currently 1)
+
+ - A number of index offset entries each consisting of:
+
+ - 32-bit offset from the begining of the file to the first cache entry
+ in this block of entries.
+
+ - 32-bit count of cache entries in this block
diff --git a/Makefile b/Makefile
index 5c8307b7c4..b08d5ea258 100644
--- a/Makefile
+++ b/Makefile
@@ -400,7 +400,7 @@ all::
# (defaults to "man") if you want to have a different default when
# "git help" is called without a parameter specifying the format.
#
-# Define TEST_GIT_INDEX_VERSION to 2, 3 or 4 to run the test suite
+# Define GIT_TEST_INDEX_VERSION to 2, 3 or 4 to run the test suite
# with a different indexfile format version. If it isn't set the index
# file format used is index-v[23].
#
@@ -590,6 +590,8 @@ XDIFF_OBJS =
VCSSVN_OBJS =
GENERATED_H =
EXTRA_CPPFLAGS =
+FUZZ_OBJS =
+FUZZ_PROGRAMS =
LIB_OBJS =
PROGRAM_OBJS =
PROGRAMS =
@@ -682,6 +684,14 @@ SCRIPTS = $(SCRIPT_SH_INS) \
ETAGS_TARGET = TAGS
+FUZZ_OBJS += fuzz-pack-headers.o
+FUZZ_OBJS += fuzz-pack-idx.o
+
+# Always build fuzz objects even if not testing, to prevent bit-rot.
+all:: $(FUZZ_OBJS)
+
+FUZZ_PROGRAMS += $(patsubst %.o,%,$(FUZZ_OBJS))
+
# Empty...
EXTRA_PROGRAMS =
@@ -1796,6 +1806,7 @@ ifndef V
QUIET_MSGFMT = @echo ' ' MSGFMT $@;
QUIET_GCOV = @echo ' ' GCOV $@;
QUIET_SP = @echo ' ' SP $<;
+ QUIET_HDR = @echo ' ' HDR $<;
QUIET_RC = @echo ' ' RC $@;
QUIET_SUBDIR0 = +@subdir=
QUIET_SUBDIR1 = ;$(NO_SUBDIR) echo ' ' SUBDIR $$subdir; \
@@ -2252,6 +2263,7 @@ TEST_OBJS := $(patsubst %$X,%.o,$(TEST_PROGRAMS)) $(patsubst %,t/helper/%,$(TEST
OBJECTS := $(LIB_OBJS) $(BUILTIN_OBJS) $(PROGRAM_OBJS) $(TEST_OBJS) \
$(XDIFF_OBJS) \
$(VCSSVN_OBJS) \
+ $(FUZZ_OBJS) \
common-main.o \
git.o
ifndef NO_CURL
@@ -2609,8 +2621,8 @@ endif
ifdef GIT_INTEROP_MAKE_OPTS
@echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
endif
-ifdef TEST_GIT_INDEX_VERSION
- @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
+ifdef GIT_TEST_INDEX_VERSION
+ @echo GIT_TEST_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(GIT_TEST_INDEX_VERSION)))'\' >>$@+
endif
@if cmp $@+ $@ >/dev/null 2>&1; then $(RM) $@+; else mv $@+ $@; fi
@@ -2678,6 +2690,17 @@ $(SP_OBJ): %.sp: %.c GIT-CFLAGS FORCE
.PHONY: sparse $(SP_OBJ)
sparse: $(SP_OBJ)
+GEN_HDRS := command-list.h unicode-width.h
+EXCEPT_HDRS := $(GEN_HDRS) compat% xdiff%
+CHK_HDRS = $(filter-out $(EXCEPT_HDRS),$(patsubst ./%,%,$(LIB_H)))
+HCO = $(patsubst %.h,%.hco,$(CHK_HDRS))
+
+$(HCO): %.hco: %.h FORCE
+ $(QUIET_HDR)$(CC) -include git-compat-util.h -I. -o /dev/null -c -xc $<
+
+.PHONY: hdr-check $(HCO)
+hdr-check: $(HCO)
+
.PHONY: style
style:
git clang-format --style file --diff --extensions c,h
@@ -2939,6 +2962,7 @@ clean: profile-clean coverage-clean cocciclean
$(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
$(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
$(RM) $(TEST_PROGRAMS) $(NO_INSTALL)
+ $(RM) $(FUZZ_PROGRAMS)
$(RM) -r bin-wrappers $(dep_dirs)
$(RM) -r po/build/
$(RM) *.pyc *.pyo */*.pyc */*.pyo command-list.h $(ETAGS_TARGET) tags cscope*
@@ -3063,3 +3087,24 @@ cover_db: coverage-report
cover_db_html: cover_db
cover -report html -outputdir cover_db_html cover_db
+
+### Fuzz testing
+#
+# Building fuzz targets generally requires a special set of compiler flags that
+# are not necessarily appropriate for general builds, and that vary greatly
+# depending on the compiler version used.
+#
+# An example command to build against libFuzzer from LLVM 4.0.0:
+#
+# make CC=clang CXX=clang++ \
+# CFLAGS="-fsanitize-coverage=trace-pc-guard -fsanitize=address" \
+# LIB_FUZZING_ENGINE=/usr/lib/llvm-4.0/lib/libFuzzer.a \
+# fuzz-all
+#
+.PHONY: fuzz-all
+
+$(FUZZ_PROGRAMS): all
+ $(QUIET_LINK)$(CXX) $(CFLAGS) $(LIB_OBJS) $(BUILTIN_OBJS) \
+ $(XDIFF_OBJS) $(EXTLIBS) git.o $@.o $(LIB_FUZZING_ENGINE) -o $@
+
+fuzz-all: $(FUZZ_PROGRAMS)
diff --git a/apply.c b/apply.c
index e485fbc6bc..fdae1d423b 100644
--- a/apply.c
+++ b/apply.c
@@ -2131,10 +2131,12 @@ static int parse_chunk(struct apply_state *state, char *buffer, unsigned long si
if (!use_patch(state, patch))
patch->ws_rule = 0;
+ else if (patch->new_name)
+ patch->ws_rule = whitespace_rule(state->repo->index,
+ patch->new_name);
else
- patch->ws_rule = whitespace_rule(patch->new_name
- ? patch->new_name
- : patch->old_name);
+ patch->ws_rule = whitespace_rule(state->repo->index,
+ patch->old_name);
patchsize = parse_single_patch(state,
buffer + offset + hdrsize,
@@ -3467,7 +3469,8 @@ static int load_preimage(struct apply_state *state,
return 0;
}
-static int three_way_merge(struct image *image,
+static int three_way_merge(struct apply_state *state,
+ struct image *image,
char *path,
const struct object_id *base,
const struct object_id *ours,
@@ -3483,7 +3486,9 @@ static int three_way_merge(struct image *image,
status = ll_merge(&result, path,
&base_file, "base",
&our_file, "ours",
- &their_file, "theirs", NULL);
+ &their_file, "theirs",
+ state->repo->index,
+ NULL);
free(base_file.ptr);
free(our_file.ptr);
free(their_file.ptr);
@@ -3595,7 +3600,7 @@ static int try_threeway(struct apply_state *state,
clear_image(&tmp_image);
/* in-core three-way merge between post and our using pre as base */
- status = three_way_merge(image, patch->new_name,
+ status = three_way_merge(state, image, patch->new_name,
&pre_oid, &our_oid, &post_oid);
if (status < 0) {
if (state->apply_verbosity > verbosity_silent)
@@ -4627,7 +4632,7 @@ static int write_out_results(struct apply_state *state, struct patch *list)
}
string_list_clear(&cpath, 0);
- rerere(0);
+ repo_rerere(state->repo, 0);
}
return errs;
diff --git a/archive-zip.c b/archive-zip.c
index 5a62351ab1..155ee4a779 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -264,9 +264,10 @@ static int has_only_ascii(const char *s)
}
}
-static int entry_is_binary(const char *path, const void *buffer, size_t size)
+static int entry_is_binary(struct index_state *istate, const char *path,
+ const void *buffer, size_t size)
{
- struct userdiff_driver *driver = userdiff_find_by_path(path);
+ struct userdiff_driver *driver = userdiff_find_by_path(istate, path);
if (!driver)
driver = userdiff_find_by_name("default");
if (driver->binary != -1)
@@ -352,7 +353,8 @@ static int write_zip_entry(struct archiver_args *args,
return error(_("cannot read %s"),
oid_to_hex(oid));
crc = crc32(crc, buffer, size);
- is_binary = entry_is_binary(path_without_prefix,
+ is_binary = entry_is_binary(args->repo->index,
+ path_without_prefix,
buffer, size);
out = buffer;
}
@@ -428,7 +430,8 @@ static int write_zip_entry(struct archiver_args *args,
break;
crc = crc32(crc, buf, readlen);
if (is_binary == -1)
- is_binary = entry_is_binary(path_without_prefix,
+ is_binary = entry_is_binary(args->repo->index,
+ path_without_prefix,
buf, readlen);
write_or_die(1, buf, readlen);
}
@@ -460,7 +463,8 @@ static int write_zip_entry(struct archiver_args *args,
break;
crc = crc32(crc, buf, readlen);
if (is_binary == -1)
- is_binary = entry_is_binary(path_without_prefix,
+ is_binary = entry_is_binary(args->repo->index,
+ path_without_prefix,
buf, readlen);
zstream.next_in = buf;
diff --git a/archive.c b/archive.c
index c1870105eb..9d16b7fadf 100644
--- a/archive.c
+++ b/archive.c
@@ -392,7 +392,7 @@ static void parse_treeish_arg(const char **argv,
if (get_oid(name, &oid))
die("Not a valid object name");
- commit = lookup_commit_reference_gently(the_repository, &oid, 1);
+ commit = lookup_commit_reference_gently(ar_args->repo, &oid, 1);
if (commit) {
commit_sha1 = commit->object.oid.hash;
archive_time = commit->date;
diff --git a/bisect.c b/bisect.c
index e8b17cf7e1..487675c672 100644
--- a/bisect.c
+++ b/bisect.c
@@ -633,7 +633,7 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
struct argv_array rev_argv = ARGV_ARRAY_INIT;
int i;
- init_revisions(revs, prefix);
+ repo_init_revisions(the_repository, revs, prefix);
revs->abbrev = 0;
revs->commit_format = CMIT_FMT_UNSPECIFIED;
@@ -890,7 +890,7 @@ static void show_diff_tree(const char *prefix, struct commit *commit)
struct rev_info opt;
/* diff-tree init */
- init_revisions(&opt, prefix);
+ repo_init_revisions(the_repository, &opt, prefix);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
opt.abbrev = 0;
opt.diff = 1;
diff --git a/blame.c b/blame.c
index d5f7b7237c..d84c937780 100644
--- a/blame.c
+++ b/blame.c
@@ -90,7 +90,7 @@ static struct blame_origin *get_origin(struct commit *commit, const char *path)
-static void verify_working_tree_path(struct repository *repo,
+static void verify_working_tree_path(struct repository *r,
struct commit *work_tree, const char *path)
{
struct commit_list *parents;
@@ -102,15 +102,15 @@ static void verify_working_tree_path(struct repository *repo,
unsigned mode;
if (!get_tree_entry(commit_oid, path, &blob_oid, &mode) &&
- oid_object_info(repo, &blob_oid, NULL) == OBJ_BLOB)
+ oid_object_info(r, &blob_oid, NULL) == OBJ_BLOB)
return;
}
- pos = index_name_pos(repo->index, path, strlen(path));
+ pos = index_name_pos(r->index, path, strlen(path));
if (pos >= 0)
; /* path is in the index */
- else if (-1 - pos < repo->index->cache_nr &&
- !strcmp(repo->index->cache[-1 - pos]->name, path))
+ else if (-1 - pos < r->index->cache_nr &&
+ !strcmp(r->index->cache[-1 - pos]->name, path))
; /* path is in the index, unmerged */
else
die("no such path '%s' in HEAD", path);
@@ -166,7 +166,7 @@ static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
* Prepare a dummy commit that represents the work tree (or staged) item.
* Note that annotating work tree item never works in the reverse.
*/
-static struct commit *fake_working_tree_commit(struct repository *repo,
+static struct commit *fake_working_tree_commit(struct repository *r,
struct diff_options *opt,
const char *path,
const char *contents_from)
@@ -183,7 +183,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
unsigned mode;
struct strbuf msg = STRBUF_INIT;
- read_index(repo->index);
+ read_index(r->index);
time(&now);
commit = alloc_commit_node(the_repository);
commit->object.parsed = 1;
@@ -195,7 +195,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
parent_tail = append_parent(parent_tail, &head_oid);
append_merge_parents(parent_tail);
- verify_working_tree_path(repo, commit, path);
+ verify_working_tree_path(r, commit, path);
origin = make_origin(commit, path);
@@ -234,7 +234,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
switch (st.st_mode & S_IFMT) {
case S_IFREG:
if (opt->flags.allow_textconv &&
- textconv_object(read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
+ textconv_object(r, read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
@@ -253,7 +253,7 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
if (strbuf_read(&buf, 0, 0) < 0)
die_errno("failed to read from stdin");
}
- convert_to_git(repo->index, path, buf.buf, buf.len, &buf, 0);
+ convert_to_git(r->index, path, buf.buf, buf.len, &buf, 0);
origin->file.ptr = buf.buf;
origin->file.size = buf.len;
pretend_object_file(buf.buf, buf.len, OBJ_BLOB, &origin->blob_oid);
@@ -264,28 +264,28 @@ static struct commit *fake_working_tree_commit(struct repository *repo,
* bits; we are not going to write this index out -- we just
* want to run "diff-index --cached".
*/
- discard_index(repo->index);
- read_index(repo->index);
+ discard_index(r->index);
+ read_index(r->index);
len = strlen(path);
if (!mode) {
- int pos = index_name_pos(repo->index, path, len);
+ int pos = index_name_pos(r->index, path, len);
if (0 <= pos)
- mode = repo->index->cache[pos]->ce_mode;
+ mode = r->index->cache[pos]->ce_mode;
else
/* Let's not bother reading from HEAD tree */
mode = S_IFREG | 0644;
}
- ce = make_empty_cache_entry(repo->index, len);
+ ce = make_empty_cache_entry(r->index, len);
oidcpy(&ce->oid, &origin->blob_oid);
memcpy(ce->name, path, len);
ce->ce_flags = create_ce_flags(0);
ce->ce_namelen = len;
ce->ce_mode = create_ce_mode(mode);
- add_index_entry(repo->index, ce,
+ add_index_entry(r->index, ce,
ADD_CACHE_OK_TO_ADD | ADD_CACHE_OK_TO_REPLACE);
- cache_tree_invalidate_path(repo->index, path);
+ cache_tree_invalidate_path(r->index, path);
return commit;
}
@@ -318,7 +318,8 @@ static void fill_origin_blob(struct diff_options *opt,
(*num_read_blob)++;
if (opt->flags.allow_textconv &&
- textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
+ textconv_object(opt->repo, o->path, o->mode,
+ &o->blob_oid, 1, &file->ptr, &file_size))
;
else
file->ptr = read_object_file(&o->blob_oid, &type,
@@ -520,14 +521,14 @@ static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porig
*
* This also fills origin->mode for corresponding tree path.
*/
-static int fill_blob_sha1_and_mode(struct repository *repo,
+static int fill_blob_sha1_and_mode(struct repository *r,
struct blame_origin *origin)
{
if (!is_null_oid(&origin->blob_oid))
return 0;
if (get_tree_entry(&origin->commit->object.oid, origin->path, &origin->blob_oid, &origin->mode))
goto error_out;
- if (oid_object_info(repo, &origin->blob_oid, NULL) != OBJ_BLOB)
+ if (oid_object_info(r, &origin->blob_oid, NULL) != OBJ_BLOB)
goto error_out;
return 0;
error_out:
@@ -540,8 +541,9 @@ static int fill_blob_sha1_and_mode(struct repository *repo,
* We have an origin -- check if the same path exists in the
* parent and return an origin structure to represent it.
*/
-static struct blame_origin *find_origin(struct commit *parent,
- struct blame_origin *origin)
+static struct blame_origin *find_origin(struct repository *r,
+ struct commit *parent,
+ struct blame_origin *origin)
{
struct blame_origin *porigin;
struct diff_options diff_opts;
@@ -561,7 +563,7 @@ static struct blame_origin *find_origin(struct commit *parent,
* and origin first. Most of the time they are the
* same and diff-tree is fairly efficient about this.
*/
- diff_setup(&diff_opts);
+ repo_diff_setup(r, &diff_opts);
diff_opts.flags.recursive = 1;
diff_opts.detect_rename = 0;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -628,14 +630,15 @@ static struct blame_origin *find_origin(struct commit *parent,
* We have an origin -- find the path that corresponds to it in its
* parent and return an origin structure to represent it.
*/
-static struct blame_origin *find_rename(struct commit *parent,
- struct blame_origin *origin)
+static struct blame_origin *find_rename(struct repository *r,
+ struct commit *parent,
+ struct blame_origin *origin)
{
struct blame_origin *porigin = NULL;
struct diff_options diff_opts;
int i;
- diff_setup(&diff_opts);
+ repo_diff_setup(r, &diff_opts);
diff_opts.flags.recursive = 1;
diff_opts.detect_rename = DIFF_DETECT_RENAME;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -1259,7 +1262,7 @@ static void find_copy_in_parent(struct blame_scoreboard *sb,
if (!unblamed)
return; /* nothing remains for this target */
- diff_setup(&diff_opts);
+ repo_diff_setup(sb->repo, &diff_opts);
diff_opts.flags.recursive = 1;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
@@ -1441,7 +1444,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,
* common cases, then we look for renames in the second pass.
*/
for (pass = 0; pass < 2 - sb->no_whole_file_rename; pass++) {
- struct blame_origin *(*find)(struct commit *, struct blame_origin *);
+ struct blame_origin *(*find)(struct repository *, struct commit *, struct blame_origin *);
find = pass ? find_rename : find_origin;
for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
@@ -1454,7 +1457,7 @@ static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin,
continue;
if (parse_commit(p))
continue;
- porigin = find(p, origin);
+ porigin = find(sb->repo, p, origin);
if (!porigin)
continue;
if (oideq(&porigin->blob_oid, &origin->blob_oid)) {
@@ -1857,7 +1860,7 @@ void setup_scoreboard(struct blame_scoreboard *sb,
die(_("no such path %s in %s"), path, final_commit_name);
if (sb->revs->diffopt.flags.allow_textconv &&
- textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
+ textconv_object(sb->repo, path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
&sb->final_buf_size))
;
else
diff --git a/builtin/add.c b/builtin/add.c
index 0b64bcdebe..ad49806ebf 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -110,7 +110,7 @@ int add_files_to_cache(const char *prefix,
memset(&data, 0, sizeof(data));
data.flags = flags;
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
if (pathspec)
copy_pathspec(&rev.prune_data, pathspec);
@@ -232,7 +232,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
if (read_cache() < 0)
die(_("Could not read the index"));
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.diffopt.context = 7;
argc = setup_revisions(argc, argv, &rev, NULL);
diff --git a/builtin/am.c b/builtin/am.c
index 5e643e2a3e..3ee9a9d2a9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1376,7 +1376,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
FILE *fp;
fp = xfopen(am_path(state, "patch"), "w");
- init_revisions(&rev_info, NULL);
+ repo_init_revisions(the_repository, &rev_info, NULL);
rev_info.diff = 1;
rev_info.abbrev = 0;
rev_info.disable_stdin = 1;
@@ -1411,7 +1411,7 @@ static void write_index_patch(const struct am_state *state)
the_repository->hash_algo->empty_tree);
fp = xfopen(am_path(state, "patch"), "w");
- init_revisions(&rev_info, NULL);
+ repo_init_revisions(the_repository, &rev_info, NULL);
rev_info.diff = 1;
rev_info.disable_stdin = 1;
rev_info.no_commit_id = 1;
@@ -1569,7 +1569,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
struct rev_info rev_info;
const char *diff_filter_str = "--diff-filter=AM";
- init_revisions(&rev_info, NULL);
+ repo_init_revisions(the_repository, &rev_info, NULL);
rev_info.diffopt.output_format = DIFF_FORMAT_NAME_STATUS;
diff_opt_parse(&rev_info.diffopt, &diff_filter_str, 1, rev_info.prefix);
add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
@@ -1608,7 +1608,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
o.verbosity = 0;
if (merge_recursive_generic(&o, &our_tree, &their_tree, 1, bases, &result)) {
- rerere(state->allow_rerere_autoupdate);
+ repo_rerere(the_repository, state->allow_rerere_autoupdate);
free(their_tree_name);
return error(_("Failed to merge in the changes."));
}
@@ -1903,7 +1903,7 @@ static void am_resolve(struct am_state *state)
goto next;
}
- rerere(0);
+ repo_rerere(the_repository, 0);
do_commit(state);
@@ -2328,7 +2328,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
/* Ensure a valid committer ident can be constructed */
git_committer_info(IDENT_STRICT);
- if (read_index_preload(&the_index, NULL) < 0)
+ if (read_index_preload(&the_index, NULL, 0) < 0)
die(_("failed to read the index"));
if (in_progress) {
diff --git a/builtin/blame.c b/builtin/blame.c
index c2da673ac8..a443af9ee9 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -830,7 +830,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
setup_default_color_by_age();
git_config(git_blame_config, &output_option);
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
revs.date_mode = blame_date_mode;
revs.diffopt.flags.allow_textconv = 1;
revs.diffopt.flags.follow_renames = 1;
@@ -1001,7 +1001,7 @@ parse_done:
long bottom, top;
if (parse_range_arg(range_list.items[range_i].string,
nth_line_cb, &sb, lno, anchor,
- &bottom, &top, sb.path))
+ &bottom, &top, sb.path, &the_index))
usage(blame_usage);
if ((!lno && (top || bottom)) || lno < bottom)
die(Q_("file %s has only %lu line",
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 64ec1745ab..8d97c84725 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -113,7 +113,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
die("git cat-file --textconv %s: <object> must be <sha1:path>",
obj_name);
- if (textconv_object(path, obj_context.mode, &oid, 1, &buf, &size))
+ if (textconv_object(the_repository, path, obj_context.mode,
+ &oid, 1, &buf, &size))
break;
/* else fallthrough */
@@ -305,7 +306,8 @@ static void print_object_or_die(struct batch_options *opt, struct expand_data *d
oid_to_hex(oid), data->rest);
} else if (opt->cmdmode == 'c') {
enum object_type type;
- if (!textconv_object(data->rest, 0100644, oid,
+ if (!textconv_object(the_repository,
+ data->rest, 0100644, oid,
1, &contents, &size))
contents = read_object_file(oid,
&type,
diff --git a/builtin/checkout.c b/builtin/checkout.c
index b30b48767e..acdafc6e4c 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -214,7 +214,8 @@ static int checkout_merged(int pos, const struct checkout *state)
* merge.renormalize set, too
*/
status = ll_merge(&result_buf, path, &ancestor, "base",
- &ours, "ours", &theirs, "theirs", NULL);
+ &ours, "ours", &theirs, "theirs",
+ state->istate, NULL);
free(ancestor.ptr);
free(ours.ptr);
free(theirs.ptr);
@@ -397,7 +398,7 @@ static void show_local_changes(struct object *head,
{
struct rev_info rev;
/* I think we want full paths, even if we're in a subdirectory. */
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
rev.diffopt.flags = opts->flags;
rev.diffopt.output_format |= DIFF_FORMAT_NAME_STATUS;
diff_setup_done(&rev.diffopt);
@@ -497,7 +498,8 @@ static int skip_merge_working_tree(const struct checkout_opts *opts,
* We must do the merge if we are actually moving to a new commit.
*/
if (!old_branch_info->commit || !new_branch_info->commit ||
- oidcmp(&old_branch_info->commit->object.oid, &new_branch_info->commit->object.oid))
+ !oideq(&old_branch_info->commit->object.oid,
+ &new_branch_info->commit->object.oid))
return 0;
/*
@@ -898,7 +900,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
struct rev_info revs;
struct object *object = &old_commit->object;
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
setup_revisions(0, NULL, &revs, NULL);
object->flags &= ~UNINTERESTING;
diff --git a/builtin/commit-graph.c b/builtin/commit-graph.c
index 0bf0c48657..c02a3f1221 100644
--- a/builtin/commit-graph.c
+++ b/builtin/commit-graph.c
@@ -64,6 +64,7 @@ static int graph_verify(int argc, const char **argv)
if (!graph)
return 0;
+ UNLEAK(graph);
return verify_commit_graph(the_repository, graph);
}
@@ -89,10 +90,8 @@ static int graph_read(int argc, const char **argv)
graph_name = get_commit_graph_filename(opts.obj_dir);
graph = load_commit_graph_one(graph_name);
- if (!graph) {
- UNLEAK(graph_name);
+ if (!graph)
die("graph file %s does not exist", graph_name);
- }
FREE_AND_NULL(graph_name);
@@ -115,11 +114,13 @@ static int graph_read(int argc, const char **argv)
printf(" large_edges");
printf("\n");
- free_commit_graph(graph);
+ UNLEAK(graph);
return 0;
}
+extern int read_replace_refs;
+
static int graph_write(int argc, const char **argv)
{
struct string_list *pack_indexes = NULL;
@@ -150,8 +151,10 @@ static int graph_write(int argc, const char **argv)
if (!opts.obj_dir)
opts.obj_dir = get_object_directory();
+ read_replace_refs = 0;
+
if (opts.reachable) {
- write_commit_graph_reachable(opts.obj_dir, opts.append);
+ write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
return 0;
}
@@ -166,14 +169,17 @@ static int graph_write(int argc, const char **argv)
pack_indexes = &lines;
if (opts.stdin_commits)
commit_hex = &lines;
+
+ UNLEAK(buf);
}
write_commit_graph(opts.obj_dir,
pack_indexes,
commit_hex,
- opts.append);
+ opts.append,
+ 1);
- string_list_clear(&lines, 0);
+ UNLEAK(lines);
return 0;
}
diff --git a/builtin/commit.c b/builtin/commit.c
index b57d8e4b82..074bd9a551 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -508,8 +508,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
wt_status_collect(s);
wt_status_print(s);
+ wt_status_collect_free_buffers(s);
- return s->commitable;
+ return s->committable;
}
static int is_a_merge(const struct commit *current_head)
@@ -655,7 +656,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
{
struct stat statbuf;
struct strbuf committer_ident = STRBUF_INIT;
- int commitable;
+ int committable;
struct strbuf sb = STRBUF_INIT;
const char *hook_arg1 = NULL;
const char *hook_arg2 = NULL;
@@ -872,8 +873,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
saved_color_setting = s->use_color;
s->use_color = 0;
- commitable = run_status(s->fp, index_file, prefix, 1, s);
+ committable = run_status(s->fp, index_file, prefix, 1, s);
s->use_color = saved_color_setting;
+ string_list_clear(&s->change, 1);
} else {
struct object_id oid;
const char *parent = "HEAD";
@@ -890,7 +892,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
for (i = 0; i < active_nr; i++)
if (ce_intent_to_add(active_cache[i]))
ita_nr++;
- commitable = active_nr - ita_nr > 0;
+ committable = active_nr - ita_nr > 0;
} else {
/*
* Unless the user did explicitly request a submodule
@@ -906,7 +908,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
if (ignore_submodule_arg &&
!strcmp(ignore_submodule_arg, "all"))
flags.ignore_submodules = 1;
- commitable = index_differs_from(parent, &flags, 1);
+ committable = index_differs_from(parent, &flags, 1);
}
}
strbuf_release(&committer_ident);
@@ -918,7 +920,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
* explicit --allow-empty. In the cherry-pick case, it may be
* empty due to conflict resolution, which the user should okay.
*/
- if (!commitable && whence != FROM_MERGE && !allow_empty &&
+ if (!committable && whence != FROM_MERGE && !allow_empty &&
!(amend && is_a_merge(current_head))) {
s->display_comment_prefix = old_display_comment_prefix;
run_status(stdout, index_file, prefix, 0, s);
@@ -982,7 +984,7 @@ static const char *find_author_by_nickname(const char *name)
const char *av[20];
int ac = 0;
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
strbuf_addf(&buf, "--author=%s", name);
av[++ac] = "--all";
av[++ac] = "-i";
@@ -1188,14 +1190,14 @@ static int parse_and_validate_options(int argc, const char *argv[],
static int dry_run_commit(int argc, const char **argv, const char *prefix,
const struct commit *current_head, struct wt_status *s)
{
- int commitable;
+ int committable;
const char *index_file;
index_file = prepare_index(argc, argv, prefix, current_head, 1);
- commitable = run_status(stdout, index_file, prefix, 0, s);
+ committable = run_status(stdout, index_file, prefix, 0, s);
rollback_index_files();
- return commitable ? 0 : 1;
+ return committable ? 0 : 1;
}
define_list_config_array_extra(color_status_slots, {"added"});
@@ -1297,6 +1299,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
static int no_renames = -1;
static const char *rename_score_arg = (const char *)-1;
static struct wt_status s;
+ unsigned int progress_flag = 0;
int fd;
struct object_id oid;
static struct option builtin_status_options[] = {
@@ -1357,8 +1360,13 @@ int cmd_status(int argc, const char **argv, const char *prefix)
PATHSPEC_PREFER_FULL,
prefix, argv);
- read_cache_preload(&s.pathspec);
- refresh_index(&the_index, REFRESH_QUIET|REFRESH_UNMERGED, &s.pathspec, NULL, NULL);
+ if (status_format != STATUS_FORMAT_PORCELAIN &&
+ status_format != STATUS_FORMAT_PORCELAIN_V2)
+ progress_flag = REFRESH_PROGRESS;
+ read_index_preload(&the_index, &s.pathspec, progress_flag);
+ refresh_index(&the_index,
+ REFRESH_QUIET|REFRESH_UNMERGED|progress_flag,
+ &s.pathspec, NULL, NULL);
if (use_optional_locks())
fd = hold_locked_index(&index_lock, 0);
@@ -1390,6 +1398,8 @@ int cmd_status(int argc, const char **argv, const char *prefix)
s.prefix = prefix;
wt_status_print(&s);
+ wt_status_collect_free_buffers(&s);
+
return 0;
}
@@ -1654,9 +1664,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"not exceeded, and then \"git reset HEAD\" to recover."));
if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0))
- write_commit_graph_reachable(get_object_directory(), 0);
+ write_commit_graph_reachable(get_object_directory(), 0, 0);
- rerere(0);
+ repo_rerere(the_repository, 0);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);
run_commit_hook(use_editor, get_index_file(), "post-commit", NULL);
if (amend && !no_post_rewrite) {
diff --git a/builtin/describe.c b/builtin/describe.c
index 22c0541da5..c48c34e866 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -488,7 +488,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
"--objects", "--in-commit-order", "--reverse", "HEAD",
NULL);
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
if (setup_revisions(args.argc, args.argv, &revs, NULL) > 1)
BUG("setup_revisions could not handle all args?");
@@ -636,7 +636,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
if (0 <= fd)
update_index_if_able(&the_index, &index_lock);
- init_revisions(&revs, prefix);
+ repo_init_revisions(the_repository, &revs, prefix);
argv_array_pushv(&args, diff_index_args);
if (setup_revisions(args.argc, args.argv, &revs, NULL) != 1)
BUG("malformed internal diff-index command line");
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index e88493ffe5..48cfcb935d 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -25,7 +25,7 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
usage(diff_files_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
precompose_argv(argc, argv);
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 522f4fdffd..fcccd1f10d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -22,7 +22,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
usage(diff_cache_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
precompose_argv(argc, argv);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index d07bf2e4c4..ef996126d7 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -110,7 +110,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
usage(diff_tree_usage);
git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
- init_revisions(opt, prefix);
+ repo_init_revisions(the_repository, opt, prefix);
if (read_cache() < 0)
die(_("index file corrupt"));
opt->abbrev = 0;
diff --git a/builtin/diff.c b/builtin/diff.c
index b3a8ba488f..f0393bba23 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -318,7 +318,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
git_config(git_diff_ui_config, NULL);
precompose_argv(argc, argv);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
if (no_index && argc != i + 2) {
if (no_index == DIFF_NO_INDEX_IMPLICIT) {
@@ -339,7 +339,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
}
if (no_index)
/* If this is a no-index diff, just run it and exit there. */
- diff_no_index(&rev, argc, argv);
+ diff_no_index(the_repository, &rev, argc, argv);
/* Otherwise, we are doing the usual "git" diff */
rev.diffopt.skip_stat_unmatch = !!diff_auto_refresh_index;
diff --git a/builtin/difftool.c b/builtin/difftool.c
index b41a9199ff..544b0e8639 100644
--- a/builtin/difftool.c
+++ b/builtin/difftool.c
@@ -112,7 +112,7 @@ static int use_wt_file(const char *workdir, const char *name,
int fd = open(buf.buf, O_RDONLY);
if (fd >= 0 &&
- !index_fd(&wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
+ !index_fd(&the_index, &wt_oid, fd, &st, OBJ_BLOB, name, 0)) {
if (is_null_oid(oid)) {
oidcpy(oid, &wt_oid);
use = 1;
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 74f3bf5c96..456797c12a 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -1033,7 +1033,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
/* we handle encodings */
git_config(git_default_config, NULL);
- init_revisions(&revs, prefix);
+ repo_init_revisions(the_repository, &revs, prefix);
init_revision_sources(&revision_sources);
revs.topo_order = 1;
revs.sources = &revision_sources;
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 0696abfc2a..8f7249f2b1 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -931,10 +931,11 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
* everything we are going to fetch already exists and is connected
* locally.
*/
-static int quickfetch(struct ref *ref_map)
+static int check_exist_and_connected(struct ref *ref_map)
{
struct ref *rm = ref_map;
struct check_connected_options opt = CHECK_CONNECTED_INIT;
+ struct ref *r;
/*
* If we are deepening a shallow clone we already have these
@@ -945,13 +946,23 @@ static int quickfetch(struct ref *ref_map)
*/
if (deepen)
return -1;
+
+ /*
+ * check_connected() allows objects to merely be promised, but
+ * we need all direct targets to exist.
+ */
+ for (r = rm; r; r = r->next) {
+ if (!has_object_file(&r->old_oid))
+ return -1;
+ }
+
opt.quiet = 1;
return check_connected(iterate_ref_map, &rm, &opt);
}
static int fetch_refs(struct transport *transport, struct ref *ref_map)
{
- int ret = quickfetch(ref_map);
+ int ret = check_exist_and_connected(ref_map);
if (ret)
ret = transport_fetch_refs(transport, ref_map);
if (!ret)
@@ -1175,6 +1186,7 @@ static int do_fetch(struct transport *transport,
int retcode = 0;
const struct ref *remote_refs;
struct argv_array ref_prefixes = ARGV_ARRAY_INIT;
+ int must_list_refs = 1;
if (tags == TAGS_DEFAULT) {
if (transport->remote->fetch_tags == 2)
@@ -1190,17 +1202,36 @@ static int do_fetch(struct transport *transport,
goto cleanup;
}
- if (rs->nr)
+ if (rs->nr) {
+ int i;
+
refspec_ref_prefixes(rs, &ref_prefixes);
- else if (transport->remote && transport->remote->fetch.nr)
+
+ /*
+ * We can avoid listing refs if all of them are exact
+ * OIDs
+ */
+ must_list_refs = 0;
+ for (i = 0; i < rs->nr; i++) {
+ if (!rs->items[i].exact_sha1) {
+ must_list_refs = 1;
+ break;
+ }
+ }
+ } else if (transport->remote && transport->remote->fetch.nr)
refspec_ref_prefixes(&transport->remote->fetch, &ref_prefixes);
- if (ref_prefixes.argc &&
- (tags == TAGS_SET || (tags == TAGS_DEFAULT))) {
- argv_array_push(&ref_prefixes, "refs/tags/");
+ if (tags == TAGS_SET || tags == TAGS_DEFAULT) {
+ must_list_refs = 1;
+ if (ref_prefixes.argc)
+ argv_array_push(&ref_prefixes, "refs/tags/");
}
- remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+ if (must_list_refs)
+ remote_refs = transport_get_remote_refs(transport, &ref_prefixes);
+ else
+ remote_refs = NULL;
+
argv_array_clear(&ref_prefixes);
ref_map = get_ref_map(transport->remote, remote_refs, rs,
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 59a40342b6..a4615587fd 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -643,7 +643,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
struct rev_info rev;
head = lookup_commit_or_die(&head_oid, "HEAD");
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
rev.commit_format = CMIT_FMT_ONELINE;
rev.ignore_merges = 1;
rev.limited = 1;
diff --git a/builtin/gc.c b/builtin/gc.c
index 2b592260e9..871a56f1c5 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -441,10 +441,16 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
return NULL;
}
+/*
+ * Returns 0 if there was no previous error and gc can proceed, 1 if
+ * gc should not proceed due to an error in the last run. Prints a
+ * message and returns -1 if an error occured while reading gc.log
+ */
static int report_last_gc_error(void)
{
struct strbuf sb = STRBUF_INIT;
int ret = 0;
+ ssize_t len;
struct stat st;
char *gc_log_path = git_pathdup("gc.log");
@@ -452,39 +458,47 @@ static int report_last_gc_error(void)
if (errno == ENOENT)
goto done;
- ret = error_errno(_("Can't stat %s"), gc_log_path);
+ ret = error_errno(_("cannot stat '%s'"), gc_log_path);
goto done;
}
if (st.st_mtime < gc_log_expire_time)
goto done;
- ret = strbuf_read_file(&sb, gc_log_path, 0);
- if (ret > 0)
- ret = error(_("The last gc run reported the following. "
+ len = strbuf_read_file(&sb, gc_log_path, 0);
+ if (len < 0)
+ ret = error_errno(_("cannot read '%s'"), gc_log_path);
+ else if (len > 0) {
+ /*
+ * A previous gc failed. Report the error, and don't
+ * bother with an automatic gc run since it is likely
+ * to fail in the same way.
+ */
+ warning(_("The last gc run reported the following. "
"Please correct the root cause\n"
"and remove %s.\n"
"Automatic cleanup will not be performed "
"until the file is removed.\n\n"
"%s"),
gc_log_path, sb.buf);
+ ret = 1;
+ }
strbuf_release(&sb);
done:
free(gc_log_path);
return ret;
}
-static int gc_before_repack(void)
+static void gc_before_repack(void)
{
if (pack_refs && run_command_v_opt(pack_refs_cmd.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, pack_refs_cmd.argv[0]);
+ die(FAILED_RUN, pack_refs_cmd.argv[0]);
if (prune_reflogs && run_command_v_opt(reflog.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, reflog.argv[0]);
+ die(FAILED_RUN, reflog.argv[0]);
pack_refs = 0;
prune_reflogs = 0;
- return 0;
}
int cmd_gc(int argc, const char **argv, const char *prefix)
@@ -565,13 +579,17 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
fprintf(stderr, _("See \"git help gc\" for manual housekeeping.\n"));
}
if (detach_auto) {
- if (report_last_gc_error())
- return -1;
+ int ret = report_last_gc_error();
+ if (ret < 0)
+ /* an I/O error occured, already reported */
+ exit(128);
+ if (ret == 1)
+ /* Last gc --auto failed. Skip this one. */
+ return 0;
if (lock_repo_for_gc(force, &pid))
return 0;
- if (gc_before_repack())
- return -1;
+ gc_before_repack(); /* dies on failure */
delete_tempfile(&pidfile);
/*
@@ -611,13 +629,12 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
atexit(process_log_file_at_exit);
}
- if (gc_before_repack())
- return -1;
+ gc_before_repack();
if (!repository_format_precious_objects) {
close_all_packs(the_repository->objects);
if (run_command_v_opt(repack.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, repack.argv[0]);
+ die(FAILED_RUN, repack.argv[0]);
if (prune_expire) {
argv_array_push(&prune, prune_expire);
@@ -627,18 +644,18 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
argv_array_push(&prune,
"--exclude-promisor-objects");
if (run_command_v_opt(prune.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, prune.argv[0]);
+ die(FAILED_RUN, prune.argv[0]);
}
}
if (prune_worktrees_expire) {
argv_array_push(&prune_worktrees, prune_worktrees_expire);
if (run_command_v_opt(prune_worktrees.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, prune_worktrees.argv[0]);
+ die(FAILED_RUN, prune_worktrees.argv[0]);
}
if (run_command_v_opt(rerere.argv, RUN_GIT_CMD))
- return error(FAILED_RUN, rerere.argv[0]);
+ die(FAILED_RUN, rerere.argv[0]);
report_garbage = report_pack_garbage;
reprepare_packed_git(the_repository);
@@ -646,7 +663,8 @@ 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(), 0);
+ write_commit_graph_reachable(get_object_directory(), 0,
+ !quiet && !daemonized);
if (auto_gc && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; "
diff --git a/builtin/grep.c b/builtin/grep.c
index 601f801158..d8508ddf79 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -103,7 +103,8 @@ static void add_work(struct grep_opt *opt, const struct grep_source *gs)
todo[todo_end].source = *gs;
if (opt->binary != GREP_BINARY_TEXT)
- grep_source_load_driver(&todo[todo_end].source);
+ grep_source_load_driver(&todo[todo_end].source,
+ opt->repo->index);
todo[todo_end].done = 0;
strbuf_reset(&todo[todo_end].out);
todo_end = (todo_end + 1) % ARRAY_SIZE(todo);
@@ -811,6 +812,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
GREP_BINARY_NOMATCH),
OPT_BOOL(0, "textconv", &opt.allow_textconv,
N_("process binary files with textconv filters")),
+ OPT_SET_INT('r', "recursive", &opt.max_depth,
+ N_("search in subdirectories (default)"), -1),
{ OPTION_INTEGER, 0, "max-depth", &opt.max_depth, N_("depth"),
N_("descend at most <depth> levels"), PARSE_OPT_NONEG,
NULL, 1 },
@@ -904,9 +907,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
OPT_END()
};
- init_grep_defaults();
+ init_grep_defaults(the_repository);
git_config(grep_cmd_config, NULL);
- grep_init(&opt, prefix);
+ grep_init(&opt, the_repository, prefix);
/*
* If there is no -- then the paths must exist in the working
diff --git a/builtin/hash-object.c b/builtin/hash-object.c
index 9ada4f4dfd..d6f06ea32f 100644
--- a/builtin/hash-object.c
+++ b/builtin/hash-object.c
@@ -40,7 +40,7 @@ static void hash_fd(int fd, const char *type, const char *path, unsigned flags,
if (fstat(fd, &st) < 0 ||
(literally
? hash_literally(&oid, fd, type, flags)
- : index_fd(&oid, fd, &st, type_from_string(type), path, flags)))
+ : index_fd(&the_index, &oid, fd, &st, type_from_string(type), path, flags)))
die((flags & HASH_WRITE_OBJECT)
? "Unable to add %s to database"
: "Unable to hash %s", path);
diff --git a/builtin/help.c b/builtin/help.c
index 8d4f6dd301..7739a5c155 100644
--- a/builtin/help.c
+++ b/builtin/help.c
@@ -38,7 +38,7 @@ static const char *html_path;
static int show_all = 0;
static int show_guides = 0;
static int show_config;
-static int verbose;
+static int verbose = 1;
static unsigned int colopts;
static enum help_format help_format = HELP_FORMAT_NONE;
static int exclude_guides;
@@ -415,9 +415,37 @@ static const char *check_git_cmd(const char* cmd)
alias = alias_lookup(cmd);
if (alias) {
- printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
- free(alias);
- exit(0);
+ const char **argv;
+ int count;
+
+ /*
+ * handle_builtin() in git.c rewrites "git cmd --help"
+ * to "git help --exclude-guides cmd", so we can use
+ * exclude_guides to distinguish "git cmd --help" from
+ * "git help cmd". In the latter case, or if cmd is an
+ * alias for a shell command, just print the alias
+ * definition.
+ */
+ if (!exclude_guides || alias[0] == '!') {
+ printf_ln(_("'%s' is aliased to '%s'"), cmd, alias);
+ free(alias);
+ exit(0);
+ }
+ /*
+ * Otherwise, we pretend that the command was "git
+ * word0 --help". We use split_cmdline() to get the
+ * first word of the alias, to ensure that we use the
+ * same rules as when the alias is actually
+ * used. split_cmdline() modifies alias in-place.
+ */
+ fprintf_ln(stderr, _("'%s' is aliased to '%s'"), cmd, alias);
+ count = split_cmdline(alias, &argv);
+ if (count < 0)
+ die(_("bad alias.%s string: %s"), cmd,
+ split_cmdline_strerror(count));
+ free(argv);
+ UNLEAK(alias);
+ return alias;
}
if (exclude_guides)
diff --git a/builtin/log.c b/builtin/log.c
index 1dbb9d829b..061d4fd864 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -118,7 +118,7 @@ static int log_line_range_callback(const struct option *option, const char *arg,
static void init_log_defaults(void)
{
- init_grep_defaults();
+ init_grep_defaults(the_repository);
init_diff_ui_defaults();
decoration_style = auto_decoration_style();
@@ -470,7 +470,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
init_log_defaults();
git_config(git_log_config, NULL);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.diff = 1;
rev.simplify_history = 0;
memset(&opt, 0, sizeof(opt));
@@ -510,7 +510,8 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
&oidc, &obj_context))
die(_("Not a valid object name %s"), obj_name);
if (!obj_context.path ||
- !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
+ !textconv_object(the_repository, obj_context.path,
+ obj_context.mode, &oidc, 1, &buf, &size)) {
free(obj_context.path);
return stream_blob_to_fd(1, oid, NULL, 0);
}
@@ -587,7 +588,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
git_config(git_log_config, NULL);
memset(&match_all, 0, sizeof(match_all));
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.diff = 1;
rev.always_show_header = 1;
rev.no_walk = REVISION_WALK_NO_WALK_SORTED;
@@ -667,7 +668,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
init_log_defaults();
git_config(git_log_config, NULL);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
init_reflog_walk(&rev.reflog_info);
rev.verbose_header = 1;
memset(&opt, 0, sizeof(opt));
@@ -706,7 +707,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
init_log_defaults();
git_config(git_log_config, NULL);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.always_show_header = 1;
memset(&opt, 0, sizeof(opt));
opt.def = "HEAD";
@@ -916,10 +917,10 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
die(_("Not a range."));
- init_patch_ids(ids);
+ init_patch_ids(the_repository, ids);
/* given a range a..b get all patch ids for b..a */
- init_revisions(&check_rev, rev->prefix);
+ repo_init_revisions(the_repository, &check_rev, rev->prefix);
check_rev.max_parents = 1;
o1->flags ^= UNINTERESTING;
o2->flags ^= UNINTERESTING;
@@ -1377,13 +1378,13 @@ static void prepare_bases(struct base_tree_info *bases,
return;
init_commit_base(&commit_base);
- diff_setup(&diffopt);
+ repo_diff_setup(the_repository, &diffopt);
diffopt.flags.recursive = 1;
diff_setup_done(&diffopt);
oidcpy(&bases->base_commit, &base->object.oid);
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
revs.max_parents = 1;
revs.topo_order = 1;
for (i = 0; i < total; i++) {
@@ -1588,7 +1589,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
extra_cc.strdup_strings = 1;
init_log_defaults();
git_config(git_format_config, NULL);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.commit_format = CMIT_FMT_EMAIL;
rev.expand_tabs_in_log_default = 0;
rev.verbose_header = 1;
@@ -2038,7 +2039,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
}
}
- init_revisions(&revs, prefix);
+ repo_init_revisions(the_repository, &revs, prefix);
revs.max_parents = 1;
if (add_pending_commit(head, &revs, 0))
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 8cea8a74f2..8fc108d305 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -76,7 +76,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
their = NULL;
if (entry)
their = entry->blob;
- return merge_blobs(path, base, our, their, size);
+ return merge_blobs(&the_index, path, base, our, their, size);
}
static void *origin(struct merge_list *entry, unsigned long *size)
diff --git a/builtin/merge.c b/builtin/merge.c
index e331ca6d48..4aa6071598 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -390,7 +390,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
printf(_("Squash commit -- not updating HEAD\n"));
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
rev.ignore_merges = 1;
rev.commit_format = CMIT_FMT_MEDIUM;
@@ -453,7 +453,7 @@ static void finish(struct commit *head_commit,
}
if (new_head && show_diffstat) {
struct diff_options opts;
- diff_setup(&opts);
+ repo_diff_setup(the_repository, &opts);
opts.stat_width = -1; /* use full terminal width */
opts.stat_graph_width = -1; /* respect statGraphWidth config */
opts.output_format |=
@@ -729,8 +729,9 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
die(_("unable to write %s"), get_index_file());
return clean ? 0 : 1;
} else {
- return try_merge_command(strategy, xopts_nr, xopts,
- common, head_arg, remoteheads);
+ return try_merge_command(the_repository,
+ strategy, xopts_nr, xopts,
+ common, head_arg, remoteheads);
}
}
@@ -899,7 +900,7 @@ static int suggest_conflicts(void)
fputs(msgbuf.buf, fp);
strbuf_release(&msgbuf);
fclose(fp);
- rerere(allow_rerere_auto);
+ repo_rerere(the_repository, allow_rerere_auto);
printf(_("Automatic merge failed; "
"fix conflicts and then commit the result.\n"));
return 1;
@@ -911,7 +912,7 @@ static int evaluate_result(void)
struct rev_info rev;
/* Check how many files differ. */
- init_revisions(&rev, "");
+ repo_init_revisions(the_repository, &rev, "");
setup_revisions(0, NULL, &rev, NULL);
rev.diffopt.output_format |=
DIFF_FORMAT_CALLBACK;
@@ -1471,7 +1472,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
goto done;
}
- if (checkout_fast_forward(&head_commit->object.oid,
+ if (checkout_fast_forward(the_repository,
+ &head_commit->object.oid,
&commit->object.oid,
overwrite_ignore)) {
ret = 1;
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index e6316d294d..b059b86aee 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -1469,6 +1469,57 @@ static void cleanup_preferred_base(void)
done_pbase_paths_num = done_pbase_paths_alloc = 0;
}
+/*
+ * Return 1 iff the object specified by "delta" can be sent
+ * literally as a delta against the base in "base_sha1". If
+ * so, then *base_out will point to the entry in our packing
+ * list, or NULL if we must use the external-base list.
+ *
+ * Depth value does not matter - find_deltas() will
+ * never consider reused delta as the base object to
+ * deltify other objects against, in order to avoid
+ * circular deltas.
+ */
+static int can_reuse_delta(const unsigned char *base_sha1,
+ struct object_entry *delta,
+ struct object_entry **base_out)
+{
+ struct object_entry *base;
+
+ if (!base_sha1)
+ return 0;
+
+ /*
+ * First see if we're already sending the base (or it's explicitly in
+ * our "excluded" list).
+ */
+ base = packlist_find(&to_pack, base_sha1, NULL);
+ if (base) {
+ if (!in_same_island(&delta->idx.oid, &base->idx.oid))
+ return 0;
+ *base_out = base;
+ return 1;
+ }
+
+ /*
+ * Otherwise, reachability bitmaps may tell us if the receiver has it,
+ * even if it was buried too deep in history to make it into the
+ * packing list.
+ */
+ if (thin && bitmap_has_sha1_in_uninteresting(bitmap_git, base_sha1)) {
+ if (use_delta_islands) {
+ struct object_id base_oid;
+ hashcpy(base_oid.hash, base_sha1);
+ if (!in_same_island(&delta->idx.oid, &base_oid))
+ return 0;
+ }
+ *base_out = NULL;
+ return 1;
+ }
+
+ return 0;
+}
+
static void check_object(struct object_entry *entry)
{
unsigned long canonical_size;
@@ -1555,22 +1606,7 @@ static void check_object(struct object_entry *entry)
break;
}
- if (base_ref && (
- (base_entry = packlist_find(&to_pack, base_ref, NULL)) ||
- (thin &&
- bitmap_has_sha1_in_uninteresting(bitmap_git, base_ref))) &&
- in_same_island(&entry->idx.oid, &base_entry->idx.oid)) {
- /*
- * If base_ref was set above that means we wish to
- * reuse delta data, and either we found that object in
- * the list of objects we want to pack, or it's one we
- * know the receiver has.
- *
- * Depth value does not matter - find_deltas() will
- * never consider reused delta as the base object to
- * deltify other objects against, in order to avoid
- * circular deltas.
- */
+ if (can_reuse_delta(base_ref, entry, &base_entry)) {
oe_set_type(entry, entry->in_pack_type);
SET_SIZE(entry, in_pack_size); /* delta size */
SET_DELTA_SIZE(entry, in_pack_size);
@@ -3070,7 +3106,7 @@ static void get_object_list(int ac, const char **av)
char line[1000];
int flags = 0;
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
save_commit_buffer = 0;
setup_revisions(ac, av, &revs, NULL);
diff --git a/builtin/prune.c b/builtin/prune.c
index 4916a4daa2..41230f8215 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -120,7 +120,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
save_commit_buffer = 0;
read_replace_refs = 0;
ref_paranoia = 1;
- init_revisions(&revs, prefix);
+ repo_init_revisions(the_repository, &revs, prefix);
argc = parse_options(argc, argv, prefix, options, prune_usage, 0);
diff --git a/builtin/pull.c b/builtin/pull.c
index b2055d1dd6..798ecf7faf 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -563,7 +563,9 @@ static int pull_into_void(const struct object_id *merge_head,
* index/worktree changes that the user already made on the unborn
* branch.
*/
- if (checkout_fast_forward(the_hash_algo->empty_tree, merge_head, 0))
+ if (checkout_fast_forward(the_repository,
+ the_hash_algo->empty_tree,
+ merge_head, 0))
return 1;
if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
@@ -916,7 +918,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
"fast-forwarding your working tree from\n"
"commit %s."), oid_to_hex(&orig_head));
- if (checkout_fast_forward(&orig_head, &curr_head, 0))
+ if (checkout_fast_forward(the_repository, &orig_head,
+ &curr_head, 0))
die(_("Cannot fast-forward your working tree.\n"
"After making sure that you saved anything precious from\n"
"$ git diff %s\n"
@@ -942,7 +945,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
int ret = 0;
if ((recurse_submodules == RECURSE_SUBMODULES_ON ||
recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND) &&
- submodule_touches_in_range(&rebase_fork_point, &curr_head))
+ submodule_touches_in_range(&the_index, &rebase_fork_point, &curr_head))
die(_("cannot rebase with locally recorded submodule modifications"));
if (!autostash) {
struct commit_list *list = NULL;
diff --git a/builtin/range-diff.c b/builtin/range-diff.c
index 96af537493..f01a0be851 100644
--- a/builtin/range-diff.c
+++ b/builtin/range-diff.c
@@ -28,7 +28,7 @@ int cmd_range_diff(int argc, const char **argv, const char *prefix)
git_config(git_diff_ui_config, NULL);
- diff_setup(&diffopt);
+ repo_diff_setup(the_repository, &diffopt);
argc = parse_options(argc, argv, NULL, options,
builtin_range_diff_usage, PARSE_OPT_KEEP_UNKNOWN |
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 4d30001950..7f089be11e 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -281,8 +281,7 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
return 0;
}
-static void show_one_alternate_ref(const char *refname,
- const struct object_id *oid,
+static void show_one_alternate_ref(const struct object_id *oid,
void *data)
{
struct oidset *seen = data;
@@ -1834,7 +1833,7 @@ static void prepare_shallow_update(struct command *commands,
/*
* keep hooks happy by forcing a temporary shallow file via
* env variable because we can't add --shallow-file to every
- * command. check_everything_connected() will be done with
+ * command. check_connected() will be done with
* true .git/shallow though.
*/
setenv(GIT_SHALLOW_FILE_ENVIRONMENT, alt_shallow_file, 1);
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 3acef5a0ab..b5941c1ff3 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -567,7 +567,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
* from reflog if the repository was pruned with older git.
*/
if (cb.cmd.stalefix) {
- init_revisions(&cb.cmd.revs, prefix);
+ repo_init_revisions(the_repository, &cb.cmd.revs, prefix);
if (flags & EXPIRE_REFLOGS_VERBOSE)
printf("Marking reachable objects...");
mark_reachable_objects(&cb.cmd.revs, 0, 0, NULL);
diff --git a/builtin/replace.c b/builtin/replace.c
index 8e67e09819..a58b9c6d13 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -39,7 +39,8 @@ struct show_data {
enum replace_format format;
};
-static int show_reference(const char *refname, const struct object_id *oid,
+static int show_reference(struct repository *r, const char *refname,
+ const struct object_id *oid,
int flag, void *cb_data)
{
struct show_data *data = cb_data;
@@ -56,9 +57,8 @@ static int show_reference(const char *refname, const struct object_id *oid,
if (get_oid(refname, &object))
return error(_("failed to resolve '%s' as a valid ref"), refname);
- obj_type = oid_object_info(the_repository, &object,
- NULL);
- repl_type = oid_object_info(the_repository, oid, NULL);
+ obj_type = oid_object_info(r, &object, NULL);
+ repl_type = oid_object_info(r, oid, NULL);
printf("%s (%s) -> %s (%s)\n", refname, type_name(obj_type),
oid_to_hex(oid), type_name(repl_type));
@@ -295,7 +295,7 @@ static int import_object(struct object_id *oid, enum object_type type,
close(fd);
return -1;
}
- if (index_fd(oid, fd, &st, type, NULL, flags) < 0)
+ if (index_fd(&the_index, oid, fd, &st, type, NULL, flags) < 0)
return error(_("unable to write object to database"));
/* index_fd close()s fd for us */
}
diff --git a/builtin/rerere.c b/builtin/rerere.c
index 5ed941b91f..e89ccbc524 100644
--- a/builtin/rerere.c
+++ b/builtin/rerere.c
@@ -70,7 +70,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
flags = RERERE_NOAUTOUPDATE;
if (argc < 1)
- return rerere(flags);
+ return repo_rerere(the_repository, flags);
if (!strcmp(argv[0], "forget")) {
struct pathspec pathspec;
@@ -78,7 +78,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
warning(_("'git rerere forget' without paths is deprecated"));
parse_pathspec(&pathspec, 0, PATHSPEC_PREFER_CWD,
prefix, argv + 1);
- return rerere_forget(&pathspec);
+ return rerere_forget(the_repository, &pathspec);
}
if (!strcmp(argv[0], "clear")) {
@@ -91,7 +91,7 @@ int cmd_rerere(int argc, const char **argv, const char *prefix)
for (i = 0; i < merge_rr.nr; i++)
printf("%s\n", merge_rr.items[i].string);
} else if (!strcmp(argv[0], "remaining")) {
- rerere_remaining(&merge_rr);
+ rerere_remaining(the_repository, &merge_rr);
for (i = 0; i < merge_rr.nr; i++) {
if (merge_rr.items[i].util != RERERE_RESOLVED)
printf("%s\n", merge_rr.items[i].string);
diff --git a/builtin/reset.c b/builtin/reset.c
index 11cd0dcb8c..6d37a35e2e 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -159,6 +159,7 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.format_callback = update_index_from_diff;
opt.format_callback_data = &intent_to_add;
opt.flags.override_submodule_config = 1;
+ opt.repo = the_repository;
if (do_diff_cache(tree_oid, &opt))
return 1;
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index ed0ea7dc5b..cc1b70522f 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -370,7 +370,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
usage(rev_list_usage);
git_config(git_default_config, NULL);
- init_revisions(&revs, prefix);
+ repo_init_revisions(the_repository, &revs, prefix);
revs.abbrev = DEFAULT_ABBREV;
revs.commit_format = CMIT_FMT_UNSPECIFIED;
diff --git a/builtin/revert.c b/builtin/revert.c
index 9a66720cfc..c93393c89b 100644
--- a/builtin/revert.c
+++ b/builtin/revert.c
@@ -174,7 +174,7 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
} else {
struct setup_revision_opt s_r_opt;
opts->revs = xmalloc(sizeof(*opts->revs));
- init_revisions(opts->revs, NULL);
+ repo_init_revisions(the_repository, opts->revs, NULL);
opts->revs->no_walk = REVISION_WALK_NO_WALK_UNSORTED;
if (argc < 2)
usage_with_options(usage_str, options);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 3898a2c9c4..88f88e97b2 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -278,7 +278,7 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
git_config(git_default_config, NULL);
shortlog_init(&log);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
parse_options_start(&ctx, argc, argv, prefix, options,
PARSE_OPT_KEEP_DASHDASH | PARSE_OPT_KEEP_ARGV0);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 247881189f..80474c3ff5 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -792,7 +792,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
path, NULL);
git_config(git_diff_basic_config, NULL);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
rev.abbrev = 0;
diff_files_args.argc = setup_revisions(diff_files_args.argc,
diff_files_args.argv,
diff --git a/builtin/update-index.c b/builtin/update-index.c
index e7fab78b3b..07c10bcb7d 100644
--- a/builtin/update-index.c
+++ b/builtin/update-index.c
@@ -282,7 +282,7 @@ static int add_one_path(const struct cache_entry *old, const char *path, int len
fill_stat_cache_info(ce, st);
ce->ce_mode = ce_mode_from_stat(old, st->st_mode);
- if (index_path(&ce->oid, path, st,
+ if (index_path(&the_index, &ce->oid, path, st,
info_only ? 0 : HASH_WRITE_OBJECT)) {
discard_cache_entry(ce);
return -1;
diff --git a/bundle.c b/bundle.c
index 14f2cfc248..1ef584b93b 100644
--- a/bundle.c
+++ b/bundle.c
@@ -140,7 +140,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
int i, ret = 0, req_nr;
const char *message = _("Repository lacks these prerequisite commits:");
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
for (i = 0; i < p->nr; i++) {
struct ref_list_entry *e = p->list + i;
struct object *o = parse_object(the_repository, &e->oid);
@@ -441,7 +441,7 @@ int create_bundle(struct bundle_header *header, const char *path,
/* init revs to list objects for pack-objects later */
save_commit_buffer = 0;
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
/* write prerequisites */
if (compute_and_write_prerequisites(bundle_fd, &revs, argc, argv))
diff --git a/cache-tree.c b/cache-tree.c
index 5ce51468f0..9d454d24bc 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -326,6 +326,7 @@ static int update_one(struct cache_tree *it,
unsigned mode;
int expected_missing = 0;
int contains_ita = 0;
+ int ce_missing_ok;
path = ce->name;
pathlen = ce_namelen(ce);
@@ -355,8 +356,11 @@ static int update_one(struct cache_tree *it,
i++;
}
+ ce_missing_ok = mode == S_IFGITLINK || missing_ok ||
+ (repository_format_partial_clone &&
+ ce_skip_worktree(ce));
if (is_null_oid(oid) ||
- (mode != S_IFGITLINK && !missing_ok && !has_object_file(oid))) {
+ (!ce_missing_ok && !has_object_file(oid))) {
strbuf_release(&buffer);
if (expected_missing)
return -1;
@@ -781,7 +785,7 @@ static void verify_one(struct index_state *istate,
strbuf_add(&tree_buf, oid->hash, the_hash_algo->rawsz);
}
hash_object_file(tree_buf.buf, tree_buf.len, tree_type, &new_oid);
- if (oidcmp(&new_oid, &it->oid))
+ if (!oideq(&new_oid, &it->oid))
BUG("cache-tree for path %.*s does not match. "
"Expected %s got %s", len, path->buf,
oid_to_hex(&new_oid), oid_to_hex(&it->oid));
diff --git a/cache.h b/cache.h
index d508f3d4f8..f7fabdde8f 100644
--- a/cache.h
+++ b/cache.h
@@ -410,7 +410,7 @@ void validate_cache_entries(const struct index_state *istate);
#define read_cache() read_index(&the_index)
#define read_cache_from(path) read_index_from(&the_index, (path), (get_git_dir()))
-#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec))
+#define read_cache_preload(pathspec) read_index_preload(&the_index, (pathspec), 0)
#define is_cache_unborn() is_index_unborn(&the_index)
#define read_cache_unmerged() read_index_unmerged(&the_index)
#define discard_cache() discard_index(&the_index)
@@ -659,7 +659,9 @@ extern int daemonize(void);
/* Initialize and use the cache information */
struct lock_file;
extern int read_index(struct index_state *);
-extern int read_index_preload(struct index_state *, const struct pathspec *pathspec);
+extern int read_index_preload(struct index_state *,
+ const struct pathspec *pathspec,
+ unsigned int refresh_flags);
extern int do_read_index(struct index_state *istate, const char *path,
int must_exist); /* for testting only! */
extern int read_index_from(struct index_state *, const char *path,
@@ -703,7 +705,7 @@ extern int unmerged_index(const struct index_state *);
* provided, the space-separated list of files that differ will be appended
* to it.
*/
-extern int index_has_changes(const struct index_state *istate,
+extern int index_has_changes(struct index_state *istate,
struct tree *tree,
struct strbuf *sb);
@@ -781,14 +783,16 @@ extern void *read_blob_data_from_index(const struct index_state *, const char *,
#define CE_MATCH_REFRESH 0x10
/* don't refresh_fsmonitor state or do stat comparison even if CE_FSMONITOR_VALID is true */
#define CE_MATCH_IGNORE_FSMONITOR 0X20
+extern int is_racy_timestamp(const struct index_state *istate,
+ const struct cache_entry *ce);
extern int ie_match_stat(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
extern int ie_modified(struct index_state *, const struct cache_entry *, struct stat *, unsigned int);
#define HASH_WRITE_OBJECT 1
#define HASH_FORMAT_CHECK 2
#define HASH_RENORMALIZE 4
-extern int index_fd(struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
-extern int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags);
+extern int index_fd(struct index_state *istate, struct object_id *oid, int fd, struct stat *st, enum object_type type, const char *path, unsigned flags);
+extern int index_path(struct index_state *istate, struct object_id *oid, const char *path, struct stat *st, unsigned flags);
/*
* Record to sd the data from st that we use to check whether a file
@@ -814,6 +818,7 @@ extern void fill_stat_cache_info(struct cache_entry *ce, struct stat *st);
#define REFRESH_IGNORE_MISSING 0x0008 /* ignore non-existent */
#define REFRESH_IGNORE_SUBMODULES 0x0010 /* ignore submodules */
#define REFRESH_IN_PORCELAIN 0x0020 /* user friendly output, not "needs update" */
+#define REFRESH_PROGRESS 0x0040 /* show progress bar if stderr is tty */
extern int refresh_index(struct index_state *, unsigned int flags, const struct pathspec *pathspec, char *seen, const char *header_msg);
extern struct cache_entry *refresh_cache_entry(struct index_state *, struct cache_entry *, unsigned int);
@@ -1705,7 +1710,7 @@ void shift_tree_by(const struct object_id *, const struct object_id *, struct ob
/* All WS_* -- when extended, adapt diff.c emit_symbol */
#define WS_RULE_MASK 07777
extern unsigned whitespace_rule_cfg;
-extern unsigned whitespace_rule(const char *);
+extern unsigned whitespace_rule(struct index_state *, const char *);
extern unsigned parse_whitespace_rule(const char *);
extern unsigned ws_check(const char *line, int len, unsigned ws_rule);
extern void ws_check_emit(const char *line, int len, unsigned ws_rule, FILE *stream, const char *set, const char *reset, const char *ws);
@@ -1727,10 +1732,12 @@ extern struct startup_info *startup_info;
/* merge.c */
struct commit_list;
-int try_merge_command(const char *strategy, size_t xopts_nr,
+int try_merge_command(struct repository *r,
+ const char *strategy, size_t xopts_nr,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes);
-int checkout_fast_forward(const struct object_id *from,
+int checkout_fast_forward(struct repository *r,
+ const struct object_id *from,
const struct object_id *to,
int overwrite_ignore);
diff --git a/combine-diff.c b/combine-diff.c
index 0fed4ca529..10155e0ec8 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -285,7 +285,8 @@ static struct lline *coalesce_lines(struct lline *base, int *lenbase,
return base;
}
-static char *grab_blob(const struct object_id *oid, unsigned int mode,
+static char *grab_blob(struct repository *r,
+ const struct object_id *oid, unsigned int mode,
unsigned long *size, struct userdiff_driver *textconv,
const char *path)
{
@@ -304,7 +305,7 @@ static char *grab_blob(const struct object_id *oid, unsigned int mode,
} else if (textconv) {
struct diff_filespec *df = alloc_filespec(path);
fill_filespec(df, oid, 1, mode);
- *size = fill_textconv(textconv, df, &blob);
+ *size = fill_textconv(r, textconv, df, &blob);
free_filespec(df);
} else {
blob = read_object_file(oid, &type, size);
@@ -389,7 +390,8 @@ static void consume_line(void *state_, char *line, unsigned long len)
}
}
-static void combine_diff(const struct object_id *parent, unsigned int mode,
+static void combine_diff(struct repository *r,
+ const struct object_id *parent, unsigned int mode,
mmfile_t *result_file,
struct sline *sline, unsigned int cnt, int n,
int num_parent, int result_deleted,
@@ -407,7 +409,7 @@ static void combine_diff(const struct object_id *parent, unsigned int mode,
if (result_deleted)
return; /* result deleted */
- parent_file.ptr = grab_blob(parent, mode, &sz, textconv, path);
+ parent_file.ptr = grab_blob(r, parent, mode, &sz, textconv, path);
parent_file.size = sz;
memset(&xpp, 0, sizeof(xpp));
xpp.flags = flags;
@@ -985,7 +987,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
const char *line_prefix = diff_line_prefix(opt);
context = opt->context;
- userdiff = userdiff_find_by_path(elem->path);
+ userdiff = userdiff_find_by_path(opt->repo->index, elem->path);
if (!userdiff)
userdiff = userdiff_find_by_name("default");
if (opt->flags.allow_textconv)
@@ -993,7 +995,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
/* Read the result of merge first */
if (!working_tree_file)
- result = grab_blob(&elem->oid, elem->mode, &result_size,
+ result = grab_blob(opt->repo, &elem->oid, elem->mode, &result_size,
textconv, elem->path);
else {
/* Used by diff-tree to read from the working tree */
@@ -1016,15 +1018,16 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
} else if (S_ISDIR(st.st_mode)) {
struct object_id oid;
if (resolve_gitlink_ref(elem->path, "HEAD", &oid) < 0)
- result = grab_blob(&elem->oid, elem->mode,
- &result_size, NULL, NULL);
+ result = grab_blob(opt->repo, &elem->oid,
+ elem->mode, &result_size,
+ NULL, NULL);
else
- result = grab_blob(&oid, elem->mode,
+ result = grab_blob(opt->repo, &oid, elem->mode,
&result_size, NULL, NULL);
} else if (textconv) {
struct diff_filespec *df = alloc_filespec(elem->path);
fill_filespec(df, &null_oid, 0, st.st_mode);
- result_size = fill_textconv(textconv, df, &result);
+ result_size = fill_textconv(opt->repo, textconv, df, &result);
free_filespec(df);
} else if (0 <= (fd = open(elem->path, O_RDONLY))) {
size_t len = xsize_t(st.st_size);
@@ -1054,7 +1057,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
if (is_file) {
struct strbuf buf = STRBUF_INIT;
- if (convert_to_git(&the_index, elem->path, result, len, &buf, global_conv_flags_eol)) {
+ if (convert_to_git(rev->diffopt.repo->index,
+ elem->path, result, len, &buf, global_conv_flags_eol)) {
free(result);
result = strbuf_detach(&buf, &len);
result_size = len;
@@ -1089,7 +1093,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
for (i = 0; !is_binary && i < num_parent; i++) {
char *buf;
unsigned long size;
- buf = grab_blob(&elem->parent[i].oid,
+ buf = grab_blob(opt->repo,
+ &elem->parent[i].oid,
elem->parent[i].mode,
&size, NULL, NULL);
if (buffer_is_binary(buf, size))
@@ -1145,7 +1150,8 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
}
}
if (i <= j)
- combine_diff(&elem->parent[i].oid,
+ combine_diff(opt->repo,
+ &elem->parent[i].oid,
elem->parent[i].mode,
&result_file, sline,
cnt, i, num_parent, result_deleted,
diff --git a/command-list.txt b/command-list.txt
index c36ea3c182..3a9af104b5 100644
--- a/command-list.txt
+++ b/command-list.txt
@@ -62,7 +62,7 @@ git-check-mailmap purehelpers
git-checkout mainporcelain history
git-checkout-index plumbingmanipulators
git-check-ref-format purehelpers
-git-cherry ancillaryinterrogators complete
+git-cherry plumbinginterrogators complete
git-cherry-pick mainporcelain
git-citool mainporcelain
git-clean mainporcelain
@@ -96,7 +96,7 @@ git-for-each-ref plumbinginterrogators
git-format-patch mainporcelain
git-fsck ancillaryinterrogators complete
git-gc mainporcelain
-git-get-tar-commit-id ancillaryinterrogators
+git-get-tar-commit-id plumbinginterrogators
git-grep mainporcelain info
git-gui mainporcelain
git-hash-object plumbingmanipulators
@@ -153,7 +153,7 @@ git-rerere ancillaryinterrogators
git-reset mainporcelain worktree
git-revert mainporcelain
git-rev-list plumbinginterrogators
-git-rev-parse ancillaryinterrogators
+git-rev-parse plumbinginterrogators
git-rm mainporcelain worktree
git-send-email foreignscminterface complete
git-send-pack synchingrepositories
diff --git a/commit-graph.c b/commit-graph.c
index 7f4519ec3b..40c855f185 100644
--- a/commit-graph.c
+++ b/commit-graph.c
@@ -13,6 +13,9 @@
#include "commit-graph.h"
#include "object-store.h"
#include "alloc.h"
+#include "hashmap.h"
+#include "replace-object.h"
+#include "progress.h"
#define GRAPH_SIGNATURE 0x43475048 /* "CGPH" */
#define GRAPH_CHUNKID_OIDFANOUT 0x4f494446 /* "OIDF" */
@@ -56,6 +59,28 @@ static struct commit_graph *alloc_commit_graph(void)
return g;
}
+extern int read_replace_refs;
+
+static int commit_graph_compatible(struct repository *r)
+{
+ if (!r->gitdir)
+ return 0;
+
+ if (read_replace_refs) {
+ prepare_replace_object(r);
+ if (hashmap_get_size(&r->objects->replace_map->map))
+ return 0;
+ }
+
+ prepare_commit_graft(r);
+ if (r->parsed_objects && r->parsed_objects->grafts_nr)
+ return 0;
+ if (is_repository_shallow(r))
+ return 0;
+
+ return 1;
+}
+
struct commit_graph *load_commit_graph_one(const char *graph_file)
{
void *graph_map;
@@ -224,6 +249,9 @@ static int prepare_commit_graph(struct repository *r)
*/
return 0;
+ if (!commit_graph_compatible(r))
+ return 0;
+
obj_dir = r->objects->objectdir;
prepare_commit_graph_one(r, obj_dir);
prepare_alt_odb(r);
@@ -252,10 +280,10 @@ int generation_numbers_enabled(struct repository *r)
return !!first_generation;
}
-static void close_commit_graph(void)
+void close_commit_graph(struct repository *r)
{
- free_commit_graph(the_repository->objects->commit_graph);
- the_repository->objects->commit_graph = NULL;
+ free_commit_graph(r->objects->commit_graph);
+ r->objects->commit_graph = NULL;
}
static int bsearch_graph(struct commit_graph *g, struct object_id *oid, uint32_t *pos)
@@ -567,6 +595,8 @@ struct packed_oid_list {
struct object_id *list;
int nr;
int alloc;
+ struct progress *progress;
+ int progress_done;
};
static int add_packed_commits(const struct object_id *oid,
@@ -579,6 +609,9 @@ static int add_packed_commits(const struct object_id *oid,
off_t offset = nth_packed_object_offset(pack, pos);
struct object_info oi = OBJECT_INFO_INIT;
+ if (list->progress)
+ display_progress(list->progress, ++list->progress_done);
+
oi.typep = &type;
if (packed_object_info(the_repository, pack, offset, &oi) < 0)
die(_("unable to get type of object %s"), oid_to_hex(oid));
@@ -606,12 +639,18 @@ static void add_missing_parents(struct packed_oid_list *oids, struct commit *com
}
}
-static void close_reachable(struct packed_oid_list *oids)
+static void close_reachable(struct packed_oid_list *oids, int report_progress)
{
int i;
struct commit *commit;
+ struct progress *progress = NULL;
+ int j = 0;
+ if (report_progress)
+ progress = start_delayed_progress(
+ _("Annotating commits in commit graph"), 0);
for (i = 0; i < oids->nr; i++) {
+ display_progress(progress, ++j);
commit = lookup_commit(the_repository, &oids->list[i]);
if (commit)
commit->object.flags |= UNINTERESTING;
@@ -623,6 +662,7 @@ static void close_reachable(struct packed_oid_list *oids)
* closure.
*/
for (i = 0; i < oids->nr; i++) {
+ display_progress(progress, ++j);
commit = lookup_commit(the_repository, &oids->list[i]);
if (commit && !parse_commit(commit))
@@ -630,19 +670,28 @@ static void close_reachable(struct packed_oid_list *oids)
}
for (i = 0; i < oids->nr; i++) {
+ display_progress(progress, ++j);
commit = lookup_commit(the_repository, &oids->list[i]);
if (commit)
commit->object.flags &= ~UNINTERESTING;
}
+ stop_progress(&progress);
}
-static void compute_generation_numbers(struct packed_commit_list* commits)
+static void compute_generation_numbers(struct packed_commit_list* commits,
+ int report_progress)
{
int i;
struct commit_list *list = NULL;
+ struct progress *progress = NULL;
+ if (report_progress)
+ progress = start_progress(
+ _("Computing commit graph generation numbers"),
+ commits->nr);
for (i = 0; i < commits->nr; i++) {
+ display_progress(progress, i + 1);
if (commits->list[i]->generation != GENERATION_NUMBER_INFINITY &&
commits->list[i]->generation != GENERATION_NUMBER_ZERO)
continue;
@@ -674,6 +723,7 @@ static void compute_generation_numbers(struct packed_commit_list* commits)
}
}
}
+ stop_progress(&progress);
}
static int add_ref_to_list(const char *refname,
@@ -686,19 +736,21 @@ static int add_ref_to_list(const char *refname,
return 0;
}
-void write_commit_graph_reachable(const char *obj_dir, int append)
+void write_commit_graph_reachable(const char *obj_dir, int append,
+ int report_progress)
{
- struct string_list list;
+ struct string_list list = STRING_LIST_INIT_DUP;
- string_list_init(&list, 1);
for_each_ref(add_ref_to_list, &list);
- write_commit_graph(obj_dir, NULL, &list, append);
+ write_commit_graph(obj_dir, NULL, &list, append, report_progress);
+
+ string_list_clear(&list, 0);
}
void write_commit_graph(const char *obj_dir,
struct string_list *pack_indexes,
struct string_list *commit_hex,
- int append)
+ int append, int report_progress)
{
struct packed_oid_list oids;
struct packed_commit_list commits;
@@ -711,9 +763,15 @@ void write_commit_graph(const char *obj_dir,
int num_chunks;
int num_extra_edges;
struct commit_list *parent;
+ struct progress *progress = NULL;
+
+ if (!commit_graph_compatible(the_repository))
+ return;
oids.nr = 0;
- oids.alloc = approximate_object_count() / 4;
+ oids.alloc = approximate_object_count() / 32;
+ oids.progress = NULL;
+ oids.progress_done = 0;
if (append) {
prepare_commit_graph_one(the_repository, obj_dir);
@@ -740,6 +798,11 @@ void write_commit_graph(const char *obj_dir,
int dirlen;
strbuf_addf(&packname, "%s/pack/", obj_dir);
dirlen = packname.len;
+ if (report_progress) {
+ oids.progress = start_delayed_progress(
+ _("Finding commits for commit graph"), 0);
+ oids.progress_done = 0;
+ }
for (i = 0; i < pack_indexes->nr; i++) {
struct packed_git *p;
strbuf_setlen(&packname, dirlen);
@@ -751,16 +814,23 @@ void write_commit_graph(const char *obj_dir,
die(_("error opening index for %s"), packname.buf);
for_each_object_in_pack(p, add_packed_commits, &oids, 0);
close_pack(p);
+ free(p);
}
+ stop_progress(&oids.progress);
strbuf_release(&packname);
}
if (commit_hex) {
+ if (report_progress)
+ progress = start_delayed_progress(
+ _("Finding commits for commit graph"),
+ commit_hex->nr);
for (i = 0; i < commit_hex->nr; i++) {
const char *end;
struct object_id oid;
struct commit *result;
+ display_progress(progress, i + 1);
if (commit_hex->items[i].string &&
parse_oid_hex(commit_hex->items[i].string, &oid, &end))
continue;
@@ -773,12 +843,18 @@ void write_commit_graph(const char *obj_dir,
oids.nr++;
}
}
+ stop_progress(&progress);
}
- if (!pack_indexes && !commit_hex)
+ if (!pack_indexes && !commit_hex) {
+ if (report_progress)
+ oids.progress = start_delayed_progress(
+ _("Finding commits for commit graph"), 0);
for_each_packed_object(add_packed_commits, &oids, 0);
+ stop_progress(&oids.progress);
+ }
- close_reachable(&oids);
+ close_reachable(&oids, report_progress);
QSORT(oids.list, oids.nr, commit_compare);
@@ -818,12 +894,14 @@ void write_commit_graph(const char *obj_dir,
if (commits.nr >= GRAPH_PARENT_MISSING)
die(_("too many commits to write graph"));
- compute_generation_numbers(&commits);
+ compute_generation_numbers(&commits, report_progress);
graph_name = get_commit_graph_filename(obj_dir);
- if (safe_create_leading_directories(graph_name))
+ if (safe_create_leading_directories(graph_name)) {
+ UNLEAK(graph_name);
die_errno(_("unable to create leading directories of %s"),
graph_name);
+ }
hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
f = hashfd(lk.tempfile->fd, lk.tempfile->filename.buf);
@@ -864,13 +942,13 @@ void write_commit_graph(const char *obj_dir,
write_graph_chunk_data(f, GRAPH_OID_LEN, commits.list, commits.nr);
write_graph_chunk_large_edges(f, commits.list, commits.nr);
- close_commit_graph();
+ close_commit_graph(the_repository);
finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
commit_lock_file(&lk);
+ free(graph_name);
+ free(commits.list);
free(oids.list);
- oids.alloc = 0;
- oids.nr = 0;
}
#define VERIFY_COMMIT_GRAPH_ERROR_HASH 2
@@ -897,6 +975,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
int generation_zero = 0;
struct hashfile *f;
int devnull;
+ struct progress *progress = NULL;
if (!g) {
graph_report("no commit-graph file loaded");
@@ -964,11 +1043,14 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
if (verify_commit_graph_error & ~VERIFY_COMMIT_GRAPH_ERROR_HASH)
return verify_commit_graph_error;
+ progress = start_progress(_("Verifying commits in commit graph"),
+ g->num_commits);
for (i = 0; i < g->num_commits; i++) {
struct commit *graph_commit, *odb_commit;
struct commit_list *graph_parents, *odb_parents;
uint32_t max_generation = 0;
+ display_progress(progress, i + 1);
hashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);
graph_commit = lookup_commit(r, &cur_oid);
@@ -1045,6 +1127,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g)
graph_commit->date,
odb_commit->date);
}
+ stop_progress(&progress);
return verify_commit_graph_error;
}
diff --git a/commit-graph.h b/commit-graph.h
index b050476765..9db40b4d3a 100644
--- a/commit-graph.h
+++ b/commit-graph.h
@@ -60,14 +60,16 @@ struct commit_graph *load_commit_graph_one(const char *graph_file);
*/
int generation_numbers_enabled(struct repository *r);
-void write_commit_graph_reachable(const char *obj_dir, int append);
+void write_commit_graph_reachable(const char *obj_dir, int append,
+ int report_progress);
void write_commit_graph(const char *obj_dir,
struct string_list *pack_indexes,
struct string_list *commit_hex,
- int append);
+ int append, int report_progress);
int verify_commit_graph(struct repository *r, struct commit_graph *g);
+void close_commit_graph(struct repository *);
void free_commit_graph(struct commit_graph *);
#endif
diff --git a/commit-reach.c b/commit-reach.c
index 00e5ceee6f..9f79ce0a22 100644
--- a/commit-reach.c
+++ b/commit-reach.c
@@ -426,7 +426,7 @@ struct contains_stack {
static int in_commit_list(const struct commit_list *want, struct commit *c)
{
for (; want; want = want->next)
- if (!oidcmp(&want->item->object.oid, &c->object.oid))
+ if (oideq(&want->item->object.oid, &c->object.oid))
return 1;
return 0;
}
@@ -561,7 +561,8 @@ int can_all_from_reach_with_flag(struct object_array *from,
from_one = deref_tag(the_repository, from_one,
"a from object", 0);
if (!from_one || from_one->type != OBJ_COMMIT) {
- /* no way to tell if this is reachable by
+ /*
+ * no way to tell if this is reachable by
* looking at the ancestry chain alone, so
* leave a note to ourselves not to worry about
* this object anymore.
@@ -625,10 +626,7 @@ int can_all_from_reach_with_flag(struct object_array *from,
}
cleanup:
- for (i = 0; i < nr_commits; i++) {
- clear_commit_marks(list[i], RESULT);
- clear_commit_marks(list[i], assign_flag);
- }
+ clear_commit_marks_many(nr_commits, list, RESULT | assign_flag);
free(list);
for (i = 0; i < from->nr; i++)
diff --git a/commit.c b/commit.c
index d0f199e122..dc8a39d52a 100644
--- a/commit.c
+++ b/commit.c
@@ -209,7 +209,7 @@ static int read_graft_file(struct repository *r, const char *graft_file)
return 0;
}
-static void prepare_commit_graft(struct repository *r)
+void prepare_commit_graft(struct repository *r)
{
char *graft_file;
diff --git a/commit.h b/commit.h
index 2b1a734388..1d260d62f5 100644
--- a/commit.h
+++ b/commit.h
@@ -202,6 +202,7 @@ typedef int (*each_commit_graft_fn)(const struct commit_graft *, void *);
struct commit_graft *read_graft_line(struct strbuf *line);
int register_commit_graft(struct repository *r, struct commit_graft *, int);
+void prepare_commit_graft(struct repository *r);
struct commit_graft *lookup_commit_graft(struct repository *r, const struct object_id *oid);
/* largest positive number a signed 32-bit integer can contain */
diff --git a/compat/poll/poll.c b/compat/poll/poll.c
index 7ed3fbbea1..ad5dcde439 100644
--- a/compat/poll/poll.c
+++ b/compat/poll/poll.c
@@ -29,9 +29,6 @@
#include <sys/types.h>
-/* Specification. */
-#include <poll.h>
-
#include <errno.h>
#include <limits.h>
#include <assert.h>
@@ -55,6 +52,9 @@
# include <unistd.h>
#endif
+/* Specification. */
+#include "poll.h"
+
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
diff --git a/compat/poll/poll.h b/compat/poll/poll.h
index cd1995292a..1e1597360f 100644
--- a/compat/poll/poll.h
+++ b/compat/poll/poll.h
@@ -21,6 +21,21 @@
#ifndef _GL_POLL_H
#define _GL_POLL_H
+#if defined(_WIN32_WINNT) && _WIN32_WINNT >= 0x600
+/* Vista has its own, socket-only poll() */
+#undef POLLIN
+#undef POLLPRI
+#undef POLLOUT
+#undef POLLERR
+#undef POLLHUP
+#undef POLLNVAL
+#undef POLLRDNORM
+#undef POLLRDBAND
+#undef POLLWRNORM
+#undef POLLWRBAND
+#define pollfd compat_pollfd
+#endif
+
/* fake a poll(2) environment */
#define POLLIN 0x0001 /* any readable data available */
#define POLLPRI 0x0002 /* OOB/Urgent readable data */
diff --git a/config.c b/config.c
index 3461993f0a..4051e38823 100644
--- a/config.c
+++ b/config.c
@@ -2278,7 +2278,7 @@ int git_config_get_max_percent_split_change(void)
int git_config_get_fsmonitor(void)
{
if (git_config_get_pathname("core.fsmonitor", &core_fsmonitor))
- core_fsmonitor = getenv("GIT_FSMONITOR_TEST");
+ core_fsmonitor = getenv("GIT_TEST_FSMONITOR");
if (core_fsmonitor && !*core_fsmonitor)
core_fsmonitor = NULL;
@@ -2289,6 +2289,24 @@ int git_config_get_fsmonitor(void)
return 0;
}
+int git_config_get_index_threads(void)
+{
+ int is_bool, val = 0;
+
+ val = git_env_ulong("GIT_TEST_INDEX_THREADS", 0);
+ if (val)
+ return val;
+
+ if (!git_config_get_bool_or_int("index.threads", &is_bool, &val)) {
+ if (is_bool)
+ return val ? 0 : 1;
+ else
+ return val;
+ }
+
+ return 0; /* auto */
+}
+
NORETURN
void git_die_config_linenr(const char *key, const char *filename, int linenr)
{
diff --git a/config.h b/config.h
index ab46e0165d..a06027e69b 100644
--- a/config.h
+++ b/config.h
@@ -250,6 +250,7 @@ extern int git_config_get_untracked_cache(void);
extern int git_config_get_split_index(void);
extern int git_config_get_max_percent_split_change(void);
extern int git_config_get_fsmonitor(void);
+extern int git_config_get_index_threads(void);
/* This dies if the configured or default date is in the future */
extern int git_config_get_expiry(const char *key, const char **output);
diff --git a/config.mak.uname b/config.mak.uname
index e47af72e01..8acdeb71fd 100644
--- a/config.mak.uname
+++ b/config.mak.uname
@@ -381,8 +381,6 @@ ifeq ($(uname_S),Windows)
NO_PYTHON = YesPlease
BLK_SHA1 = YesPlease
ETAGS_TARGET = ETAGS
- NO_INET_PTON = YesPlease
- NO_INET_NTOP = YesPlease
NO_POSIX_GOODIES = UnfortunatelyYes
NATIVE_CRLF = YesPlease
DEFAULT_HELP_FORMAT = html
@@ -529,8 +527,6 @@ ifneq (,$(findstring MINGW,$(uname_S)))
NO_REGEX = YesPlease
NO_PYTHON = YesPlease
ETAGS_TARGET = ETAGS
- NO_INET_PTON = YesPlease
- NO_INET_NTOP = YesPlease
NO_POSIX_GOODIES = UnfortunatelyYes
DEFAULT_HELP_FORMAT = html
COMPAT_CFLAGS += -DNOGDI -Icompat -Icompat/win32
diff --git a/connected.h b/connected.h
index e4c961817d..8d5a6b3ad6 100644
--- a/connected.h
+++ b/connected.h
@@ -51,9 +51,9 @@ struct check_connected_options {
#define CHECK_CONNECTED_INIT { 0 }
/*
- * Make sure that our object store has all the commits necessary to
- * connect the ancestry chain to some of our existing refs, and all
- * the trees and blobs that these commits use.
+ * Make sure that all given objects and all objects reachable from them
+ * either exist in our object store or (if the repository is a partial
+ * clone) are promised to be available.
*
* Return 0 if Ok, non zero otherwise (i.e. some missing objects)
*
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index d63d2dffd4..5f556075d3 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -943,6 +943,7 @@ __git_complete_remote_or_refspec ()
*) ;;
esac
;;
+ --multiple) no_complete_refspec=1; break ;;
-*) ;;
*) remote="$i"; break ;;
esac
@@ -1340,17 +1341,6 @@ _git_checkout ()
esac
}
-_git_cherry ()
-{
- case "$cur" in
- --*)
- __gitcomp_builtin cherry
- return
- esac
-
- __git_complete_refs
-}
-
__git_cherry_pick_inprogress_options="--continue --quit --abort"
_git_cherry_pick ()
@@ -2567,6 +2557,9 @@ _git_stash ()
drop,--*)
__gitcomp "--quiet"
;;
+ list,--*)
+ __gitcomp "--name-status --oneline --patch-with-stat"
+ ;;
show,--*|branch,--*)
;;
branch,*)
diff --git a/contrib/coverage-diff.sh b/contrib/coverage-diff.sh
new file mode 100755
index 0000000000..4ec419f900
--- /dev/null
+++ b/contrib/coverage-diff.sh
@@ -0,0 +1,108 @@
+#!/bin/sh
+
+# Usage: Run 'contrib/coverage-diff.sh <version1> <version2>' from source-root
+# after running
+#
+# make coverage-test
+# make coverage-report
+#
+# while checked out at <version2>. This script combines the *.gcov files
+# generated by the 'make' commands above with 'git diff <version1> <version2>'
+# to report new lines that are not covered by the test suite.
+
+V1=$1
+V2=$2
+
+diff_lines () {
+ perl -e '
+ my $line_num;
+ while (<>) {
+ # Hunk header? Grab the beginning in postimage.
+ if (/^@@ -\d+(?:,\d+)? \+(\d+)(?:,\d+)? @@/) {
+ $line_num = $1;
+ next;
+ }
+
+ # Have we seen a hunk? Ignore "diff --git" etc.
+ next unless defined $line_num;
+
+ # Deleted line? Ignore.
+ if (/^-/) {
+ next;
+ }
+
+ # Show only the line number of added lines.
+ if (/^\+/) {
+ print "$line_num\n";
+ }
+ # Either common context or added line appear in
+ # the postimage. Count it.
+ $line_num++;
+ }
+ '
+}
+
+files=$(git diff --name-only "$V1" "$V2" -- \*.c)
+
+# create empty file
+>coverage-data.txt
+
+for file in $files
+do
+ git diff "$V1" "$V2" -- "$file" |
+ diff_lines |
+ sort >new_lines.txt
+
+ if ! test -s new_lines.txt
+ then
+ continue
+ fi
+
+ hash_file=$(echo $file | sed "s/\//\#/")
+
+ if ! test -s "$hash_file.gcov"
+ then
+ continue
+ fi
+
+ sed -ne '/#####:/{
+ s/ #####://
+ s/:.*//
+ s/ //g
+ p
+ }' "$hash_file.gcov" |
+ sort >uncovered_lines.txt
+
+ comm -12 uncovered_lines.txt new_lines.txt |
+ sed -e 's/$/\)/' |
+ sed -e 's/^/ /' >uncovered_new_lines.txt
+
+ grep -q '[^[:space:]]' <uncovered_new_lines.txt &&
+ echo $file >>coverage-data.txt &&
+ git blame -s "$V2" -- "$file" |
+ sed 's/\t//g' |
+ grep -f uncovered_new_lines.txt >>coverage-data.txt &&
+ echo >>coverage-data.txt
+
+ rm -f new_lines.txt uncovered_lines.txt uncovered_new_lines.txt
+done
+
+cat coverage-data.txt
+
+echo "Commits introducing uncovered code:"
+
+commit_list=$(cat coverage-data.txt |
+ grep -E '^[0-9a-f]{7,} ' |
+ awk '{print $1;}' |
+ sort |
+ uniq)
+
+(
+ for commit in $commit_list
+ do
+ git log --no-decorate --pretty=format:'%an %h: %s' -1 $commit
+ echo
+ done
+) | sort
+
+rm coverage-data.txt
diff --git a/contrib/subtree/git-subtree.sh b/contrib/subtree/git-subtree.sh
index d3f39a862a..147201dc6c 100755
--- a/contrib/subtree/git-subtree.sh
+++ b/contrib/subtree/git-subtree.sh
@@ -231,12 +231,14 @@ cache_miss () {
}
check_parents () {
- missed=$(cache_miss "$@")
+ missed=$(cache_miss "$1")
+ local indent=$(($2 + 1))
for miss in $missed
do
if ! test -r "$cachedir/notree/$miss"
then
debug " incorrect order: $miss"
+ process_split_commit "$miss" "" "$indent"
fi
done
}
@@ -340,7 +342,12 @@ find_existing_splits () {
revs="$2"
main=
sub=
- git log --grep="^git-subtree-dir: $dir/*\$" \
+ local grep_format="^git-subtree-dir: $dir/*\$"
+ if test -n "$ignore_joins"
+ then
+ grep_format="^Add '$dir/' from commit '"
+ fi
+ git log --grep="$grep_format" \
--no-show-signature --pretty=format:'START %H%n%s%n%n%b%nEND%n' $revs |
while read a b junk
do
@@ -534,6 +541,7 @@ copy_or_skip () {
nonidentical=
p=
gotparents=
+ copycommit=
for parent in $newparents
do
ptree=$(toptree_for_commit $parent) || exit $?
@@ -541,7 +549,24 @@ copy_or_skip () {
if test "$ptree" = "$tree"
then
# an identical parent could be used in place of this rev.
- identical="$parent"
+ if test -n "$identical"
+ then
+ # if a previous identical parent was found, check whether
+ # one is already an ancestor of the other
+ mergebase=$(git merge-base $identical $parent)
+ if test "$identical" = "$mergebase"
+ then
+ # current identical commit is an ancestor of parent
+ identical="$parent"
+ elif test "$parent" != "$mergebase"
+ then
+ # no common history; commit must be copied
+ copycommit=1
+ fi
+ else
+ # first identical parent detected
+ identical="$parent"
+ fi
else
nonidentical="$parent"
fi
@@ -564,7 +589,6 @@ copy_or_skip () {
fi
done
- copycommit=
if test -n "$identical" && test -n "$nonidentical"
then
extras=$(git rev-list --count $identical..$nonidentical)
@@ -598,6 +622,58 @@ ensure_valid_ref_format () {
die "'$1' does not look like a ref"
}
+process_split_commit () {
+ local rev="$1"
+ local parents="$2"
+ local indent=$3
+
+ if test $indent -eq 0
+ then
+ revcount=$(($revcount + 1))
+ else
+ # processing commit without normal parent information;
+ # fetch from repo
+ parents=$(git rev-parse "$rev^@")
+ extracount=$(($extracount + 1))
+ fi
+
+ progress "$revcount/$revmax ($createcount) [$extracount]"
+
+ debug "Processing commit: $rev"
+ exists=$(cache_get "$rev")
+ if test -n "$exists"
+ then
+ debug " prior: $exists"
+ return
+ fi
+ createcount=$(($createcount + 1))
+ debug " parents: $parents"
+ check_parents "$parents" "$indent"
+ newparents=$(cache_get $parents)
+ debug " newparents: $newparents"
+
+ tree=$(subtree_for_commit "$rev" "$dir")
+ debug " tree is: $tree"
+
+ # ugly. is there no better way to tell if this is a subtree
+ # vs. a mainline commit? Does it matter?
+ if test -z "$tree"
+ then
+ set_notree "$rev"
+ if test -n "$newparents"
+ then
+ cache_set "$rev" "$rev"
+ fi
+ return
+ fi
+
+ newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
+ debug " newrev is: $newrev"
+ cache_set "$rev" "$newrev"
+ cache_set latest_new "$newrev"
+ cache_set latest_old "$rev"
+}
+
cmd_add () {
if test -e "$dir"
then
@@ -689,12 +765,7 @@ cmd_split () {
done
fi
- if test -n "$ignore_joins"
- then
- unrevs=
- else
- unrevs="$(find_existing_splits "$dir" "$revs")"
- fi
+ unrevs="$(find_existing_splits "$dir" "$revs")"
# We can't restrict rev-list to only $dir here, because some of our
# parents have the $dir contents the root, and those won't match.
@@ -703,45 +774,11 @@ cmd_split () {
revmax=$(eval "$grl" | wc -l)
revcount=0
createcount=0
+ extracount=0
eval "$grl" |
while read rev parents
do
- revcount=$(($revcount + 1))
- progress "$revcount/$revmax ($createcount)"
- debug "Processing commit: $rev"
- exists=$(cache_get "$rev")
- if test -n "$exists"
- then
- debug " prior: $exists"
- continue
- fi
- createcount=$(($createcount + 1))
- debug " parents: $parents"
- newparents=$(cache_get $parents)
- debug " newparents: $newparents"
-
- tree=$(subtree_for_commit "$rev" "$dir")
- debug " tree is: $tree"
-
- check_parents $parents
-
- # ugly. is there no better way to tell if this is a subtree
- # vs. a mainline commit? Does it matter?
- if test -z "$tree"
- then
- set_notree "$rev"
- if test -n "$newparents"
- then
- cache_set "$rev" "$rev"
- fi
- continue
- fi
-
- newrev=$(copy_or_skip "$rev" "$tree" "$newparents") || exit $?
- debug " newrev is: $newrev"
- cache_set "$rev" "$newrev"
- cache_set latest_new "$newrev"
- cache_set latest_old "$rev"
+ process_split_commit "$rev" "$parents" 0
done || exit $?
latest_new=$(cache_get latest_new)
diff --git a/delta-islands.h b/delta-islands.h
index f9725730f4..b635cd07d8 100644
--- a/delta-islands.h
+++ b/delta-islands.h
@@ -1,6 +1,10 @@
#ifndef DELTA_ISLANDS_H
#define DELTA_ISLANDS_H
+struct object_id;
+struct packing_data;
+struct commit;
+
int island_delta_cmp(const struct object_id *a, const struct object_id *b);
int in_same_island(const struct object_id *, const struct object_id *);
void resolve_tree_islands(int progress, struct packing_data *to_pack);
diff --git a/diff-lib.c b/diff-lib.c
index 30bf9a2399..83fce51518 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -70,7 +70,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt,
struct stat *st, unsigned ce_option,
unsigned *dirty_submodule)
{
- int changed = ce_match_stat(ce, st, ce_option);
+ int changed = ie_match_stat(diffopt->repo->index, ce, st, ce_option);
if (S_ISGITLINK(ce->ce_mode)) {
struct diff_flags orig_flags = diffopt->flags;
if (!diffopt->flags.override_submodule_config)
@@ -93,15 +93,16 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED)
? CE_MATCH_RACY_IS_DIRTY : 0);
uint64_t start = getnanotime();
+ struct index_state *istate = revs->diffopt.repo->index;
diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
if (diff_unmerged_stage < 0)
diff_unmerged_stage = 2;
- entries = active_nr;
+ entries = istate->cache_nr;
for (i = 0; i < entries; i++) {
unsigned int oldmode, newmode;
- struct cache_entry *ce = active_cache[i];
+ struct cache_entry *ce = istate->cache[i];
int changed;
unsigned dirty_submodule = 0;
const struct object_id *old_oid, *new_oid;
@@ -109,7 +110,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
if (diff_can_quit_early(&revs->diffopt))
break;
- if (!ce_path_match(&the_index, ce, &revs->prune_data, NULL))
+ if (!ce_path_match(istate, ce, &revs->prune_data, NULL))
continue;
if (ce_stage(ce)) {
@@ -145,7 +146,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
dpath->mode = wt_mode;
while (i < entries) {
- struct cache_entry *nce = active_cache[i];
+ struct cache_entry *nce = istate->cache[i];
int stage;
if (strcmp(ce->name, nce->name))
@@ -474,7 +475,9 @@ static int oneway_diff(const struct cache_entry * const *src,
if (tree == o->df_conflict_entry)
tree = NULL;
- if (ce_path_match(&the_index, idx ? idx : tree, &revs->prune_data, NULL)) {
+ if (ce_path_match(revs->diffopt.repo->index,
+ idx ? idx : tree,
+ &revs->prune_data, NULL)) {
do_oneway_diff(o, idx, tree);
if (diff_can_quit_early(&revs->diffopt)) {
o->exiting_early = 1;
@@ -506,7 +509,7 @@ static int diff_cache(struct rev_info *revs,
opts.merge = 1;
opts.fn = oneway_diff;
opts.unpack_data = revs;
- opts.src_index = &the_index;
+ opts.src_index = revs->diffopt.repo->index;
opts.dst_index = NULL;
opts.pathspec = &revs->diffopt.pathspec;
opts.pathspec->recursive = 1;
@@ -539,7 +542,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
{
struct rev_info revs;
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
copy_pathspec(&revs.prune_data, &opt->pathspec);
revs.diffopt = *opt;
@@ -554,7 +557,7 @@ int index_differs_from(const char *def, const struct diff_flags *flags,
struct rev_info rev;
struct setup_revision_opt opt;
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
memset(&opt, 0, sizeof(opt));
opt.def = def;
setup_revisions(0, NULL, &rev, &opt);
diff --git a/diff-no-index.c b/diff-no-index.c
index 0ed5f0f496..9414e922d1 100644
--- a/diff-no-index.c
+++ b/diff-no-index.c
@@ -233,7 +233,8 @@ static void fixup_paths(const char **path, struct strbuf *replacement)
}
}
-void diff_no_index(struct rev_info *revs,
+void diff_no_index(struct repository *r,
+ struct rev_info *revs,
int argc, const char **argv)
{
int i;
@@ -241,7 +242,11 @@ void diff_no_index(struct rev_info *revs,
struct strbuf replacement = STRBUF_INIT;
const char *prefix = revs->prefix;
- diff_setup(&revs->diffopt);
+ /*
+ * FIXME: --no-index should not look at index and we should be
+ * able to pass NULL repo. Maybe later.
+ */
+ repo_diff_setup(r, &revs->diffopt);
for (i = 1; i < argc - 2; ) {
int j;
if (!strcmp(argv[i], "--no-index"))
diff --git a/diff.c b/diff.c
index f0c7557b40..e82b6341ee 100644
--- a/diff.c
+++ b/diff.c
@@ -554,14 +554,15 @@ static int count_lines(const char *data, int size)
return count;
}
-static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
+static int fill_mmfile(struct repository *r, mmfile_t *mf,
+ struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one)) {
mf->ptr = (char *)""; /* does not matter */
mf->size = 0;
return 0;
}
- else if (diff_populate_filespec(one, 0))
+ else if (diff_populate_filespec(r, one, 0))
return -1;
mf->ptr = one->data;
@@ -570,11 +571,12 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
}
/* like fill_mmfile, but only for size, so we can avoid retrieving blob */
-static unsigned long diff_filespec_size(struct diff_filespec *one)
+static unsigned long diff_filespec_size(struct repository *r,
+ struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one))
return 0;
- diff_populate_filespec(one, CHECK_SIZE_ONLY);
+ diff_populate_filespec(r, one, CHECK_SIZE_ONLY);
return one->size;
}
@@ -776,7 +778,6 @@ struct moved_entry {
struct hashmap_entry ent;
const struct emitted_diff_symbol *es;
struct moved_entry *next_line;
- struct ws_delta *wsd;
};
/**
@@ -793,6 +794,17 @@ struct ws_delta {
};
#define WS_DELTA_INIT { NULL, 0 }
+struct moved_block {
+ struct moved_entry *match;
+ struct ws_delta wsd;
+};
+
+static void moved_block_clear(struct moved_block *b)
+{
+ FREE_AND_NULL(b->wsd.string);
+ b->match = NULL;
+}
+
static int compute_ws_delta(const struct emitted_diff_symbol *a,
const struct emitted_diff_symbol *b,
struct ws_delta *out)
@@ -801,16 +813,19 @@ static int compute_ws_delta(const struct emitted_diff_symbol *a,
const struct emitted_diff_symbol *shorter = a->len > b->len ? b : a;
int d = longer->len - shorter->len;
+ if (strncmp(longer->line + d, shorter->line, shorter->len))
+ return 0;
+
out->string = xmemdupz(longer->line, d);
out->current_longer = (a == longer);
- return !strncmp(longer->line + d, shorter->line, shorter->len);
+ return 1;
}
static int cmp_in_block_with_wsd(const struct diff_options *o,
const struct moved_entry *cur,
const struct moved_entry *match,
- struct moved_entry *pmb,
+ struct moved_block *pmb,
int n)
{
struct emitted_diff_symbol *l = &o->emitted_symbols->buf[n];
@@ -830,16 +845,15 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
if (strcmp(a, b))
return 1;
- if (!pmb->wsd)
+ if (!pmb->wsd.string)
/*
- * No white space delta was carried forward? This can happen
- * when we exit early in this function and do not carry
- * forward ws.
+ * The white space delta is not active? This can happen
+ * when we exit early in this function.
*/
return 1;
/*
- * The indent changes of the block are known and carried forward in
+ * The indent changes of the block are known and stored in
* pmb->wsd; however we need to check if the indent changes of the
* current line are still the same as before.
*
@@ -847,8 +861,8 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
* one of them for the white spaces, depending which was longer.
*/
- wslen = strlen(pmb->wsd->string);
- if (pmb->wsd->current_longer) {
+ wslen = strlen(pmb->wsd.string);
+ if (pmb->wsd.current_longer) {
c += wslen;
cl -= wslen;
} else {
@@ -856,7 +870,7 @@ static int cmp_in_block_with_wsd(const struct diff_options *o,
al -= wslen;
}
- if (strcmp(a, c))
+ if (al != cl || memcmp(a, c, al))
return 1;
return 0;
@@ -898,7 +912,6 @@ static struct moved_entry *prepare_entry(struct diff_options *o,
ret->ent.hash = xdiff_hash_string(l->line, l->len, flags);
ret->es = l;
ret->next_line = NULL;
- ret->wsd = NULL;
return ret;
}
@@ -938,18 +951,18 @@ static void add_lines_to_move_detection(struct diff_options *o,
static void pmb_advance_or_null(struct diff_options *o,
struct moved_entry *match,
struct hashmap *hm,
- struct moved_entry **pmb,
+ struct moved_block *pmb,
int pmb_nr)
{
int i;
for (i = 0; i < pmb_nr; i++) {
- struct moved_entry *prev = pmb[i];
+ struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL;
if (cur && !hm->cmpfn(o, cur, match, NULL)) {
- pmb[i] = cur;
+ pmb[i].match = cur;
} else {
- pmb[i] = NULL;
+ pmb[i].match = NULL;
}
}
}
@@ -957,7 +970,7 @@ static void pmb_advance_or_null(struct diff_options *o,
static void pmb_advance_or_null_multi_match(struct diff_options *o,
struct moved_entry *match,
struct hashmap *hm,
- struct moved_entry **pmb,
+ struct moved_block *pmb,
int pmb_nr, int n)
{
int i;
@@ -965,49 +978,47 @@ static void pmb_advance_or_null_multi_match(struct diff_options *o,
for (; match; match = hashmap_get_next(hm, match)) {
for (i = 0; i < pmb_nr; i++) {
- struct moved_entry *prev = pmb[i];
+ struct moved_entry *prev = pmb[i].match;
struct moved_entry *cur = (prev && prev->next_line) ?
prev->next_line : NULL;
if (!cur)
continue;
- if (!cmp_in_block_with_wsd(o, cur, match, pmb[i], n))
+ if (!cmp_in_block_with_wsd(o, cur, match, &pmb[i], n))
got_match[i] |= 1;
}
}
for (i = 0; i < pmb_nr; i++) {
if (got_match[i]) {
- /* Carry the white space delta forward */
- pmb[i]->next_line->wsd = pmb[i]->wsd;
- pmb[i] = pmb[i]->next_line;
+ /* Advance to the next line */
+ pmb[i].match = pmb[i].match->next_line;
} else {
- if (pmb[i]->wsd) {
- free(pmb[i]->wsd->string);
- FREE_AND_NULL(pmb[i]->wsd);
- }
- pmb[i] = NULL;
+ moved_block_clear(&pmb[i]);
}
}
+
+ free(got_match);
}
-static int shrink_potential_moved_blocks(struct moved_entry **pmb,
+static int shrink_potential_moved_blocks(struct moved_block *pmb,
int pmb_nr)
{
int lp, rp;
/* Shrink the set of potential block to the remaining running */
for (lp = 0, rp = pmb_nr - 1; lp <= rp;) {
- while (lp < pmb_nr && pmb[lp])
+ while (lp < pmb_nr && pmb[lp].match)
lp++;
/* lp points at the first NULL now */
- while (rp > -1 && !pmb[rp])
+ while (rp > -1 && !pmb[rp].match)
rp--;
/* rp points at the last non-NULL */
if (lp < pmb_nr && rp > -1 && lp < rp) {
pmb[lp] = pmb[rp];
- pmb[rp] = NULL;
+ pmb[rp].match = NULL;
+ pmb[rp].wsd.string = NULL;
rp--;
lp++;
}
@@ -1054,7 +1065,7 @@ static void mark_color_as_moved(struct diff_options *o,
struct hashmap *add_lines,
struct hashmap *del_lines)
{
- struct moved_entry **pmb = NULL; /* potentially moved blocks */
+ struct moved_block *pmb = NULL; /* potentially moved blocks */
int pmb_nr = 0, pmb_alloc = 0;
int n, flipped_block = 1, block_length = 0;
@@ -1083,7 +1094,11 @@ static void mark_color_as_moved(struct diff_options *o,
}
if (!match) {
+ int i;
+
adjust_last_block(o, n, block_length);
+ for(i = 0; i < pmb_nr; i++)
+ moved_block_clear(&pmb[i]);
pmb_nr = 0;
block_length = 0;
continue;
@@ -1111,14 +1126,12 @@ static void mark_color_as_moved(struct diff_options *o,
ALLOC_GROW(pmb, pmb_nr + 1, pmb_alloc);
if (o->color_moved_ws_handling &
COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE) {
- struct ws_delta *wsd = xmalloc(sizeof(*match->wsd));
- if (compute_ws_delta(l, match->es, wsd)) {
- match->wsd = wsd;
- pmb[pmb_nr++] = match;
- } else
- free(wsd);
+ if (compute_ws_delta(l, match->es,
+ &pmb[pmb_nr].wsd))
+ pmb[pmb_nr++].match = match;
} else {
- pmb[pmb_nr++] = match;
+ pmb[pmb_nr].wsd.string = NULL;
+ pmb[pmb_nr++].match = match;
}
}
@@ -1135,6 +1148,8 @@ static void mark_color_as_moved(struct diff_options *o,
}
adjust_last_block(o, n, block_length);
+ for(n = 0; n < pmb_nr; n++)
+ moved_block_clear(&pmb[n]);
free(pmb);
}
@@ -1202,10 +1217,11 @@ static void dim_moved_lines(struct diff_options *o)
static void emit_line_ws_markup(struct diff_options *o,
const char *set_sign, const char *set,
const char *reset,
- char sign, const char *line, int len,
+ int sign_index, const char *line, int len,
unsigned ws_rule, int blank_at_eof)
{
const char *ws = NULL;
+ int sign = o->output_indicators[sign_index];
if (o->ws_error_highlight & ws_rule) {
ws = diff_get_color_opt(o, DIFF_WHITESPACE);
@@ -1285,8 +1301,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
set = diff_get_color_opt(o, DIFF_FILE_OLD);
}
emit_line_ws_markup(o, set_sign, set, reset,
- o->output_indicators[OUTPUT_INDICATOR_CONTEXT],
- line, len,
+ OUTPUT_INDICATOR_CONTEXT, line, len,
flags & (DIFF_SYMBOL_CONTENT_WS_MASK), 0);
break;
case DIFF_SYMBOL_PLUS:
@@ -1330,8 +1345,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
flags &= ~DIFF_SYMBOL_CONTENT_WS_MASK;
}
emit_line_ws_markup(o, set_sign, set, reset,
- o->output_indicators[OUTPUT_INDICATOR_NEW],
- line, len,
+ OUTPUT_INDICATOR_NEW, line, len,
flags & DIFF_SYMBOL_CONTENT_WS_MASK,
flags & DIFF_SYMBOL_CONTENT_BLANK_LINE_EOF);
break;
@@ -1375,8 +1389,7 @@ static void emit_diff_symbol_from_struct(struct diff_options *o,
set = diff_get_color_opt(o, DIFF_CONTEXT_DIM);
}
emit_line_ws_markup(o, set_sign, set, reset,
- o->output_indicators[OUTPUT_INDICATOR_OLD],
- line, len,
+ OUTPUT_INDICATOR_OLD, line, len,
flags & DIFF_SYMBOL_CONTENT_WS_MASK, 0);
break;
case DIFF_SYMBOL_WORDS_PORCELAIN:
@@ -1714,12 +1727,12 @@ static void emit_rewrite_diff(const char *name_a,
quote_two_c_style(&a_name, a_prefix, name_a, 0);
quote_two_c_style(&b_name, b_prefix, name_b, 0);
- size_one = fill_textconv(textconv_one, one, &data_one);
- size_two = fill_textconv(textconv_two, two, &data_two);
+ size_one = fill_textconv(o->repo, textconv_one, one, &data_one);
+ size_two = fill_textconv(o->repo, textconv_two, two, &data_two);
memset(&ecbdata, 0, sizeof(ecbdata));
ecbdata.color_diff = want_color(o->use_color);
- ecbdata.ws_rule = whitespace_rule(name_b);
+ ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
ecbdata.opt = o;
if (ecbdata.ws_rule & WS_BLANK_AT_EOF) {
mmfile_t mf1, mf2;
@@ -2107,23 +2120,25 @@ static void diff_words_flush(struct emit_callback *ecbdata)
}
}
-static void diff_filespec_load_driver(struct diff_filespec *one)
+static void diff_filespec_load_driver(struct diff_filespec *one,
+ struct index_state *istate)
{
/* Use already-loaded driver */
if (one->driver)
return;
if (S_ISREG(one->mode))
- one->driver = userdiff_find_by_path(one->path);
+ one->driver = userdiff_find_by_path(istate, one->path);
/* Fallback to default settings */
if (!one->driver)
one->driver = userdiff_find_by_name("default");
}
-static const char *userdiff_word_regex(struct diff_filespec *one)
+static const char *userdiff_word_regex(struct diff_filespec *one,
+ struct index_state *istate)
{
- diff_filespec_load_driver(one);
+ diff_filespec_load_driver(one, istate);
return one->driver->word_regex;
}
@@ -2146,9 +2161,9 @@ static void init_diff_words_data(struct emit_callback *ecbdata,
xcalloc(1, sizeof(struct emitted_diff_symbols));
if (!o->word_regex)
- o->word_regex = userdiff_word_regex(one);
+ o->word_regex = userdiff_word_regex(one, o->repo->index);
if (!o->word_regex)
- o->word_regex = userdiff_word_regex(two);
+ o->word_regex = userdiff_word_regex(two, o->repo->index);
if (!o->word_regex)
o->word_regex = diff_word_regex_cfg;
if (o->word_regex) {
@@ -2976,18 +2991,19 @@ static void show_dirstat(struct diff_options *options)
}
if (DIFF_FILE_VALID(p->one) && DIFF_FILE_VALID(p->two)) {
- diff_populate_filespec(p->one, 0);
- diff_populate_filespec(p->two, 0);
- diffcore_count_changes(p->one, p->two, NULL, NULL,
+ diff_populate_filespec(options->repo, p->one, 0);
+ diff_populate_filespec(options->repo, p->two, 0);
+ diffcore_count_changes(options->repo,
+ p->one, p->two, NULL, NULL,
&copied, &added);
diff_free_filespec_data(p->one);
diff_free_filespec_data(p->two);
} else if (DIFF_FILE_VALID(p->one)) {
- diff_populate_filespec(p->one, CHECK_SIZE_ONLY);
+ diff_populate_filespec(options->repo, p->one, CHECK_SIZE_ONLY);
copied = added = 0;
diff_free_filespec_data(p->one);
} else if (DIFF_FILE_VALID(p->two)) {
- diff_populate_filespec(p->two, CHECK_SIZE_ONLY);
+ diff_populate_filespec(options->repo, p->two, CHECK_SIZE_ONLY);
copied = 0;
added = p->two->size;
diff_free_filespec_data(p->two);
@@ -3261,15 +3277,16 @@ static void emit_binary_diff(struct diff_options *o,
emit_binary_diff_body(o, two, one);
}
-int diff_filespec_is_binary(struct diff_filespec *one)
+int diff_filespec_is_binary(struct repository *r,
+ struct diff_filespec *one)
{
if (one->is_binary == -1) {
- diff_filespec_load_driver(one);
+ diff_filespec_load_driver(one, r->index);
if (one->driver->binary != -1)
one->is_binary = one->driver->binary;
else {
if (!one->data && DIFF_FILE_VALID(one))
- diff_populate_filespec(one, CHECK_BINARY);
+ diff_populate_filespec(r, one, CHECK_BINARY);
if (one->is_binary == -1 && one->data)
one->is_binary = buffer_is_binary(one->data,
one->size);
@@ -3280,9 +3297,10 @@ int diff_filespec_is_binary(struct diff_filespec *one)
return one->is_binary;
}
-static const struct userdiff_funcname *diff_funcname_pattern(struct diff_filespec *one)
+static const struct userdiff_funcname *
+diff_funcname_pattern(struct diff_options *o, struct diff_filespec *one)
{
- diff_filespec_load_driver(one);
+ diff_filespec_load_driver(one, o->repo->index);
return one->driver->funcname.pattern ? &one->driver->funcname : NULL;
}
@@ -3294,12 +3312,13 @@ void diff_set_mnemonic_prefix(struct diff_options *options, const char *a, const
options->b_prefix = b;
}
-struct userdiff_driver *get_textconv(struct diff_filespec *one)
+struct userdiff_driver *get_textconv(struct index_state *istate,
+ struct diff_filespec *one)
{
if (!DIFF_FILE_VALID(one))
return NULL;
- diff_filespec_load_driver(one);
+ diff_filespec_load_driver(one, istate);
return userdiff_get_textconv(one->driver);
}
@@ -3349,8 +3368,8 @@ static void builtin_diff(const char *name_a,
}
if (o->flags.allow_textconv) {
- textconv_one = get_textconv(one);
- textconv_two = get_textconv(two);
+ textconv_one = get_textconv(o->repo->index, one);
+ textconv_two = get_textconv(o->repo->index, two);
}
/* Never use a non-valid filename anywhere if at all possible */
@@ -3391,13 +3410,13 @@ static void builtin_diff(const char *name_a,
if ((one->mode ^ two->mode) & S_IFMT)
goto free_ab_and_return;
if (complete_rewrite &&
- (textconv_one || !diff_filespec_is_binary(one)) &&
- (textconv_two || !diff_filespec_is_binary(two))) {
+ (textconv_one || !diff_filespec_is_binary(o->repo, one)) &&
+ (textconv_two || !diff_filespec_is_binary(o->repo, two))) {
emit_diff_symbol(o, DIFF_SYMBOL_HEADER,
header.buf, header.len, 0);
strbuf_reset(&header);
emit_rewrite_diff(name_a, name_b, one, two,
- textconv_one, textconv_two, o);
+ textconv_one, textconv_two, o);
o->found_changes = 1;
goto free_ab_and_return;
}
@@ -3409,8 +3428,8 @@ static void builtin_diff(const char *name_a,
strbuf_reset(&header);
goto free_ab_and_return;
} else if (!o->flags.text &&
- ( (!textconv_one && diff_filespec_is_binary(one)) ||
- (!textconv_two && diff_filespec_is_binary(two)) )) {
+ ( (!textconv_one && diff_filespec_is_binary(o->repo, one)) ||
+ (!textconv_two && diff_filespec_is_binary(o->repo, two)) )) {
struct strbuf sb = STRBUF_INIT;
if (!one->data && !two->data &&
S_ISREG(one->mode) && S_ISREG(two->mode) &&
@@ -3431,7 +3450,8 @@ static void builtin_diff(const char *name_a,
strbuf_release(&sb);
goto free_ab_and_return;
}
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+ fill_mmfile(o->repo, &mf2, two) < 0)
die("unable to read files to diff");
/* Quite common confusing case */
if (mf1.size == mf2.size &&
@@ -3468,12 +3488,12 @@ static void builtin_diff(const char *name_a,
strbuf_reset(&header);
}
- mf1.size = fill_textconv(textconv_one, one, &mf1.ptr);
- mf2.size = fill_textconv(textconv_two, two, &mf2.ptr);
+ mf1.size = fill_textconv(o->repo, textconv_one, one, &mf1.ptr);
+ mf2.size = fill_textconv(o->repo, textconv_two, two, &mf2.ptr);
- pe = diff_funcname_pattern(one);
+ pe = diff_funcname_pattern(o, one);
if (!pe)
- pe = diff_funcname_pattern(two);
+ pe = diff_funcname_pattern(o, two);
memset(&xpp, 0, sizeof(xpp));
memset(&xecfg, 0, sizeof(xecfg));
@@ -3482,7 +3502,7 @@ static void builtin_diff(const char *name_a,
lbl[0] = NULL;
ecbdata.label_path = lbl;
ecbdata.color_diff = want_color(o->use_color);
- ecbdata.ws_rule = whitespace_rule(name_b);
+ ecbdata.ws_rule = whitespace_rule(o->repo->index, name_b);
if (ecbdata.ws_rule & WS_BLANK_AT_EOF)
check_blank_at_eof(&mf1, &mf2, &ecbdata);
ecbdata.opt = o;
@@ -3582,20 +3602,21 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
same_contents = oideq(&one->oid, &two->oid);
- if (diff_filespec_is_binary(one) || diff_filespec_is_binary(two)) {
+ if (diff_filespec_is_binary(o->repo, one) ||
+ diff_filespec_is_binary(o->repo, two)) {
data->is_binary = 1;
if (same_contents) {
data->added = 0;
data->deleted = 0;
} else {
- data->added = diff_filespec_size(two);
- data->deleted = diff_filespec_size(one);
+ data->added = diff_filespec_size(o->repo, two);
+ data->deleted = diff_filespec_size(o->repo, one);
}
}
else if (complete_rewrite) {
- diff_populate_filespec(one, 0);
- diff_populate_filespec(two, 0);
+ diff_populate_filespec(o->repo, one, 0);
+ diff_populate_filespec(o->repo, two, 0);
data->deleted = count_lines(one->data, one->size);
data->added = count_lines(two->data, two->size);
}
@@ -3605,7 +3626,8 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
xpparam_t xpp;
xdemitconf_t xecfg;
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+ fill_mmfile(o->repo, &mf2, two) < 0)
die("unable to read files to diff");
memset(&xpp, 0, sizeof(xpp));
@@ -3640,10 +3662,11 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
data.filename = name_b ? name_b : name_a;
data.lineno = 0;
data.o = o;
- data.ws_rule = whitespace_rule(attr_path);
- data.conflict_marker_size = ll_merge_marker_size(attr_path);
+ data.ws_rule = whitespace_rule(o->repo->index, attr_path);
+ data.conflict_marker_size = ll_merge_marker_size(o->repo->index, attr_path);
- if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
+ if (fill_mmfile(o->repo, &mf1, one) < 0 ||
+ fill_mmfile(o->repo, &mf2, two) < 0)
die("unable to read files to diff");
/*
@@ -3652,7 +3675,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
* introduced changes, and as long as the "new" side is text, we
* can and should check what it introduces.
*/
- if (diff_filespec_is_binary(two))
+ if (diff_filespec_is_binary(o->repo, two))
goto free_and_return;
else {
/* Crazy xdl interfaces.. */
@@ -3725,7 +3748,10 @@ void fill_filespec(struct diff_filespec *spec, const struct object_id *oid,
* the work tree has that object contents, return true, so that
* prepare_temp_file() does not have to inflate and extract.
*/
-static int reuse_worktree_file(const char *name, const struct object_id *oid, int want_file)
+static int reuse_worktree_file(struct index_state *istate,
+ const char *name,
+ const struct object_id *oid,
+ int want_file)
{
const struct cache_entry *ce;
struct stat st;
@@ -3744,7 +3770,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in
* by diff-cache --cached, which does read the cache before
* calling us.
*/
- if (!active_cache)
+ if (!istate->cache)
return 0;
/* We want to avoid the working directory if our caller
@@ -3763,14 +3789,14 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in
* Similarly, if we'd have to convert the file contents anyway, that
* makes the optimization not worthwhile.
*/
- if (!want_file && would_convert_to_git(&the_index, name))
+ if (!want_file && would_convert_to_git(istate, name))
return 0;
len = strlen(name);
- pos = cache_name_pos(name, len);
+ pos = index_name_pos(istate, name, len);
if (pos < 0)
return 0;
- ce = active_cache[pos];
+ ce = istate->cache[pos];
/*
* This is not the sha1 we are looking for, or
@@ -3790,7 +3816,7 @@ static int reuse_worktree_file(const char *name, const struct object_id *oid, in
* If ce matches the file in the work tree, we can reuse it.
*/
if (ce_uptodate(ce) ||
- (!lstat(name, &st) && !ce_match_stat(ce, &st, 0)))
+ (!lstat(name, &st) && !ie_match_stat(istate, ce, &st, 0)))
return 1;
return 0;
@@ -3823,7 +3849,9 @@ static int diff_populate_gitlink(struct diff_filespec *s, int size_only)
* grab the data for the blob (or file) for our own in-core comparison.
* diff_filespec has data and size fields for this purpose.
*/
-int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
+int diff_populate_filespec(struct repository *r,
+ struct diff_filespec *s,
+ unsigned int flags)
{
int size_only = flags & CHECK_SIZE_ONLY;
int err = 0;
@@ -3850,7 +3878,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
return diff_populate_gitlink(s, size_only);
if (!s->oid_valid ||
- reuse_worktree_file(s->path, &s->oid, 0)) {
+ reuse_worktree_file(r->index, s->path, &s->oid, 0)) {
struct strbuf buf = STRBUF_INIT;
struct stat st;
int fd;
@@ -3883,7 +3911,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
* point if the path requires us to run the content
* conversion.
*/
- if (size_only && !would_convert_to_git(&the_index, s->path))
+ if (size_only && !would_convert_to_git(r->index, s->path))
return 0;
/*
@@ -3910,7 +3938,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
/*
* Convert from working tree format to canonical git format
*/
- if (convert_to_git(&the_index, s->path, s->data, s->size, &buf, conv_flags)) {
+ if (convert_to_git(r->index, s->path, s->data, s->size, &buf, conv_flags)) {
size_t size = 0;
munmap(s->data, s->size);
s->should_munmap = 0;
@@ -3922,8 +3950,7 @@ int diff_populate_filespec(struct diff_filespec *s, unsigned int flags)
else {
enum object_type type;
if (size_only || (flags & CHECK_BINARY)) {
- type = oid_object_info(the_repository, &s->oid,
- &s->size);
+ type = oid_object_info(r, &s->oid, &s->size);
if (type < 0)
die("unable to read %s",
oid_to_hex(&s->oid));
@@ -3961,7 +3988,8 @@ void diff_free_filespec_data(struct diff_filespec *s)
FREE_AND_NULL(s->cnt_data);
}
-static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
+static void prep_temp_blob(struct index_state *istate,
+ const char *path, struct diff_tempfile *temp,
void *blob,
unsigned long size,
const struct object_id *oid,
@@ -3979,7 +4007,7 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
temp->tempfile = mks_tempfile_ts(tempfile.buf, strlen(base) + 1);
if (!temp->tempfile)
die_errno("unable to create temp-file");
- if (convert_to_working_tree(&the_index, path,
+ if (convert_to_working_tree(istate, path,
(const char *)blob, (size_t)size, &buf)) {
blob = buf.buf;
size = buf.len;
@@ -3995,8 +4023,9 @@ static void prep_temp_blob(const char *path, struct diff_tempfile *temp,
free(path_dup);
}
-static struct diff_tempfile *prepare_temp_file(const char *name,
- struct diff_filespec *one)
+static struct diff_tempfile *prepare_temp_file(struct repository *r,
+ const char *name,
+ struct diff_filespec *one)
{
struct diff_tempfile *temp = claim_diff_tempfile();
@@ -4013,7 +4042,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
if (!S_ISGITLINK(one->mode) &&
(!one->oid_valid ||
- reuse_worktree_file(name, &one->oid, 1))) {
+ reuse_worktree_file(r->index, name, &one->oid, 1))) {
struct stat st;
if (lstat(name, &st) < 0) {
if (errno == ENOENT)
@@ -4024,7 +4053,7 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
struct strbuf sb = STRBUF_INIT;
if (strbuf_readlink(&sb, name, st.st_size) < 0)
die_errno("readlink(%s)", name);
- prep_temp_blob(name, temp, sb.buf, sb.len,
+ prep_temp_blob(r->index, name, temp, sb.buf, sb.len,
(one->oid_valid ?
&one->oid : &null_oid),
(one->oid_valid ?
@@ -4049,19 +4078,21 @@ static struct diff_tempfile *prepare_temp_file(const char *name,
return temp;
}
else {
- if (diff_populate_filespec(one, 0))
+ if (diff_populate_filespec(r, one, 0))
die("cannot read data blob for %s", one->path);
- prep_temp_blob(name, temp, one->data, one->size,
+ prep_temp_blob(r->index, name, temp,
+ one->data, one->size,
&one->oid, one->mode);
}
return temp;
}
-static void add_external_diff_name(struct argv_array *argv,
+static void add_external_diff_name(struct repository *r,
+ struct argv_array *argv,
const char *name,
struct diff_filespec *df)
{
- struct diff_tempfile *temp = prepare_temp_file(name, df);
+ struct diff_tempfile *temp = prepare_temp_file(r, name, df);
argv_array_push(argv, temp->name);
argv_array_push(argv, temp->hex);
argv_array_push(argv, temp->mode);
@@ -4090,11 +4121,11 @@ static void run_external_diff(const char *pgm,
argv_array_push(&argv, name);
if (one && two) {
- add_external_diff_name(&argv, name, one);
+ add_external_diff_name(o->repo, &argv, name, one);
if (!other)
- add_external_diff_name(&argv, name, two);
+ add_external_diff_name(o->repo, &argv, name, two);
else {
- add_external_diff_name(&argv, other, two);
+ add_external_diff_name(o->repo, &argv, other, two);
argv_array_push(&argv, other);
argv_array_push(&argv, xfrm_msg);
}
@@ -4187,8 +4218,10 @@ static void fill_metainfo(struct strbuf *msg,
if (o->flags.binary) {
mmfile_t mf;
- if ((!fill_mmfile(&mf, one) && diff_filespec_is_binary(one)) ||
- (!fill_mmfile(&mf, two) && diff_filespec_is_binary(two)))
+ if ((!fill_mmfile(o->repo, &mf, one) &&
+ diff_filespec_is_binary(o->repo, one)) ||
+ (!fill_mmfile(o->repo, &mf, two) &&
+ diff_filespec_is_binary(o->repo, two)))
abbrev = hexsz;
}
strbuf_addf(msg, "%s%sindex %s..%s", line_prefix, set,
@@ -4216,7 +4249,9 @@ static void run_diff_cmd(const char *pgm,
if (o->flags.allow_external) {
- struct userdiff_driver *drv = userdiff_find_by_path(attr_path);
+ struct userdiff_driver *drv;
+
+ drv = userdiff_find_by_path(o->repo->index, attr_path);
if (drv && drv->external)
pgm = drv->external;
}
@@ -4245,7 +4280,7 @@ static void run_diff_cmd(const char *pgm,
fprintf(o->file, "* Unmerged path %s\n", name);
}
-static void diff_fill_oid_info(struct diff_filespec *one)
+static void diff_fill_oid_info(struct diff_filespec *one, struct index_state *istate)
{
if (DIFF_FILE_VALID(one)) {
if (!one->oid_valid) {
@@ -4256,7 +4291,7 @@ static void diff_fill_oid_info(struct diff_filespec *one)
}
if (lstat(one->path, &st) < 0)
die_errno("stat '%s'", one->path);
- if (index_path(&one->oid, one->path, &st, 0))
+ if (index_path(istate, &one->oid, one->path, &st, 0))
die("cannot hash %s", one->path);
}
}
@@ -4304,8 +4339,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
return;
}
- diff_fill_oid_info(one);
- diff_fill_oid_info(two);
+ diff_fill_oid_info(one, o->repo->index);
+ diff_fill_oid_info(two, o->repo->index);
if (!pgm &&
DIFF_FILE_VALID(one) && DIFF_FILE_VALID(two) &&
@@ -4316,7 +4351,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
*/
struct diff_filespec *null = alloc_filespec(two->path);
run_diff_cmd(NULL, name, other, attr_path,
- one, null, &msg, o, p);
+ one, null, &msg,
+ o, p);
free(null);
strbuf_release(&msg);
@@ -4340,7 +4376,8 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */
- builtin_diffstat(p->one->path, NULL, NULL, NULL, diffstat, o, p);
+ builtin_diffstat(p->one->path, NULL, NULL, NULL,
+ diffstat, o, p);
return;
}
@@ -4350,10 +4387,11 @@ static void run_diffstat(struct diff_filepair *p, struct diff_options *o,
if (o->prefix_length)
strip_prefix(o->prefix_length, &name, &other);
- diff_fill_oid_info(p->one);
- diff_fill_oid_info(p->two);
+ diff_fill_oid_info(p->one, o->repo->index);
+ diff_fill_oid_info(p->two, o->repo->index);
- builtin_diffstat(name, other, p->one, p->two, diffstat, o, p);
+ builtin_diffstat(name, other, p->one, p->two,
+ diffstat, o, p);
}
static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
@@ -4374,17 +4412,18 @@ static void run_checkdiff(struct diff_filepair *p, struct diff_options *o)
if (o->prefix_length)
strip_prefix(o->prefix_length, &name, &other);
- diff_fill_oid_info(p->one);
- diff_fill_oid_info(p->two);
+ diff_fill_oid_info(p->one, o->repo->index);
+ diff_fill_oid_info(p->two, o->repo->index);
builtin_checkdiff(name, other, attr_path, p->one, p->two, o);
}
-void diff_setup(struct diff_options *options)
+void repo_diff_setup(struct repository *r, struct diff_options *options)
{
memcpy(options, &default_diff_options, sizeof(*options));
options->file = stdout;
+ options->repo = r;
options->output_indicators[OUTPUT_INDICATOR_NEW] = '+';
options->output_indicators[OUTPUT_INDICATOR_OLD] = '-';
@@ -5683,8 +5722,8 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
if (DIFF_PAIR_UNMERGED(p))
continue;
- diff_fill_oid_info(p->one);
- diff_fill_oid_info(p->two);
+ diff_fill_oid_info(p->one, options->repo->index);
+ diff_fill_oid_info(p->two, options->repo->index);
len1 = remove_space(p->one->path, strlen(p->one->path));
len2 = remove_space(p->two->path, strlen(p->two->path));
@@ -5716,12 +5755,12 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid
if (diff_header_only)
continue;
- if (fill_mmfile(&mf1, p->one) < 0 ||
- fill_mmfile(&mf2, p->two) < 0)
+ if (fill_mmfile(options->repo, &mf1, p->one) < 0 ||
+ fill_mmfile(options->repo, &mf2, p->two) < 0)
return error("unable to read files to diff");
- if (diff_filespec_is_binary(p->one) ||
- diff_filespec_is_binary(p->two)) {
+ if (diff_filespec_is_binary(options->repo, p->one) ||
+ diff_filespec_is_binary(options->repo, p->two)) {
git_SHA1_Update(&ctx, oid_to_hex(&p->one->oid),
GIT_SHA1_HEXSZ);
git_SHA1_Update(&ctx, oid_to_hex(&p->two->oid),
@@ -5839,8 +5878,8 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
if (o->color_moved == COLOR_MOVED_ZEBRA_DIM)
dim_moved_lines(o);
- hashmap_free(&add_lines, 0);
- hashmap_free(&del_lines, 0);
+ hashmap_free(&add_lines, 1);
+ hashmap_free(&del_lines, 1);
}
for (i = 0; i < esm.nr; i++)
@@ -6024,19 +6063,21 @@ static void diffcore_apply_filter(struct diff_options *options)
}
/* Check whether two filespecs with the same mode and size are identical */
-static int diff_filespec_is_identical(struct diff_filespec *one,
+static int diff_filespec_is_identical(struct repository *r,
+ struct diff_filespec *one,
struct diff_filespec *two)
{
if (S_ISGITLINK(one->mode))
return 0;
- if (diff_populate_filespec(one, 0))
+ if (diff_populate_filespec(r, one, 0))
return 0;
- if (diff_populate_filespec(two, 0))
+ if (diff_populate_filespec(r, two, 0))
return 0;
return !memcmp(one->data, two->data, one->size);
}
-static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
+static int diff_filespec_check_stat_unmatch(struct repository *r,
+ struct diff_filepair *p)
{
if (p->done_skip_stat_unmatch)
return p->skip_stat_unmatch_result;
@@ -6060,10 +6101,10 @@ static int diff_filespec_check_stat_unmatch(struct diff_filepair *p)
!DIFF_FILE_VALID(p->two) ||
(p->one->oid_valid && p->two->oid_valid) ||
(p->one->mode != p->two->mode) ||
- diff_populate_filespec(p->one, CHECK_SIZE_ONLY) ||
- diff_populate_filespec(p->two, CHECK_SIZE_ONLY) ||
+ diff_populate_filespec(r, p->one, CHECK_SIZE_ONLY) ||
+ diff_populate_filespec(r, p->two, CHECK_SIZE_ONLY) ||
(p->one->size != p->two->size) ||
- !diff_filespec_is_identical(p->one, p->two)) /* (2) */
+ !diff_filespec_is_identical(r, p->one, p->two)) /* (2) */
p->skip_stat_unmatch_result = 1;
return p->skip_stat_unmatch_result;
}
@@ -6078,7 +6119,7 @@ static void diffcore_skip_stat_unmatch(struct diff_options *diffopt)
for (i = 0; i < q->nr; i++) {
struct diff_filepair *p = q->queue[i];
- if (diff_filespec_check_stat_unmatch(p))
+ if (diff_filespec_check_stat_unmatch(diffopt->repo, p))
diff_q(&outq, p);
else {
/*
@@ -6120,7 +6161,8 @@ void diffcore_std(struct diff_options *options)
if (!options->found_follow) {
/* See try_to_follow_renames() in tree-diff.c */
if (options->break_opt != -1)
- diffcore_break(options->break_opt);
+ diffcore_break(options->repo,
+ options->break_opt);
if (options->detect_rename)
diffcore_rename(options);
if (options->break_opt != -1)
@@ -6271,7 +6313,7 @@ void diff_change(struct diff_options *options,
return;
if (options->flags.quick && options->skip_stat_unmatch &&
- !diff_filespec_check_stat_unmatch(p))
+ !diff_filespec_check_stat_unmatch(options->repo, p))
return;
options->flags.has_changes = 1;
@@ -6293,8 +6335,10 @@ struct diff_filepair *diff_unmerge(struct diff_options *options, const char *pat
return pair;
}
-static char *run_textconv(const char *pgm, struct diff_filespec *spec,
- size_t *outsize)
+static char *run_textconv(struct repository *r,
+ const char *pgm,
+ struct diff_filespec *spec,
+ size_t *outsize)
{
struct diff_tempfile *temp;
const char *argv[3];
@@ -6303,7 +6347,7 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
struct strbuf buf = STRBUF_INIT;
int err = 0;
- temp = prepare_temp_file(spec->path, spec);
+ temp = prepare_temp_file(r, spec->path, spec);
*arg++ = pgm;
*arg++ = temp->name;
*arg = NULL;
@@ -6330,7 +6374,8 @@ static char *run_textconv(const char *pgm, struct diff_filespec *spec,
return strbuf_detach(&buf, outsize);
}
-size_t fill_textconv(struct userdiff_driver *driver,
+size_t fill_textconv(struct repository *r,
+ struct userdiff_driver *driver,
struct diff_filespec *df,
char **outbuf)
{
@@ -6341,7 +6386,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
*outbuf = "";
return 0;
}
- if (diff_populate_filespec(df, 0))
+ if (diff_populate_filespec(r, df, 0))
die("unable to read files to diff");
*outbuf = df->data;
return df->size;
@@ -6358,7 +6403,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
return size;
}
- *outbuf = run_textconv(driver->textconv, df, &size);
+ *outbuf = run_textconv(r, driver->textconv, df, &size);
if (!*outbuf)
die("unable to read files to diff");
@@ -6378,7 +6423,8 @@ size_t fill_textconv(struct userdiff_driver *driver,
return size;
}
-int textconv_object(const char *path,
+int textconv_object(struct repository *r,
+ const char *path,
unsigned mode,
const struct object_id *oid,
int oid_valid,
@@ -6390,13 +6436,13 @@ int textconv_object(const char *path,
df = alloc_filespec(path);
fill_filespec(df, oid, oid_valid, mode);
- textconv = get_textconv(df);
+ textconv = get_textconv(r->index, df);
if (!textconv) {
free_filespec(df);
return 0;
}
- *buf_size = fill_textconv(textconv, df, buf);
+ *buf_size = fill_textconv(r, textconv, df, buf);
free_filespec(df);
return 1;
}
diff --git a/diff.h b/diff.h
index a30cc35ec3..ce5e8a8183 100644
--- a/diff.h
+++ b/diff.h
@@ -18,6 +18,7 @@ struct userdiff_driver;
struct oid_array;
struct commit;
struct combine_diff_path;
+struct repository;
typedef int (*pathchange_fn_t)(struct diff_options *options,
struct combine_diff_path *path);
@@ -225,6 +226,8 @@ struct diff_options {
/* XDF_WHITESPACE_FLAGS regarding block detection are set at 2, 3, 4 */
#define COLOR_MOVED_WS_ALLOW_INDENTATION_CHANGE (1<<5)
int color_moved_ws_handling;
+
+ struct repository *repo;
};
void diff_emit_submodule_del(struct diff_options *o, const char *line);
@@ -338,7 +341,10 @@ int git_diff_basic_config(const char *var, const char *value, void *cb);
int git_diff_heuristic_config(const char *var, const char *value, void *cb);
void init_diff_ui_defaults(void);
int git_diff_ui_config(const char *var, const char *value, void *cb);
-void diff_setup(struct diff_options *);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define diff_setup(diffopts) repo_diff_setup(the_repository, diffopts)
+#endif
+void repo_diff_setup(struct repository *, struct diff_options *);
int diff_opt_parse(struct diff_options *, const char **, int, const char *);
void diff_setup_done(struct diff_options *);
int git_config_rename(const char *var, const char *value);
@@ -428,7 +434,7 @@ int diff_flush_patch_id(struct diff_options *, struct object_id *, int);
int diff_result_code(struct diff_options *, int);
-void diff_no_index(struct rev_info *, int, const char **);
+void diff_no_index(struct repository *, struct rev_info *, int, const char **);
int index_differs_from(const char *def, const struct diff_flags *flags,
int ita_invisible_in_index);
@@ -444,7 +450,8 @@ int index_differs_from(const char *def, const struct diff_flags *flags,
* struct. If it is non-NULL, then "outbuf" points to a newly allocated buffer
* that should be freed by the caller.
*/
-size_t fill_textconv(struct userdiff_driver *driver,
+size_t fill_textconv(struct repository *r,
+ struct userdiff_driver *driver,
struct diff_filespec *df,
char **outbuf);
@@ -453,14 +460,19 @@ size_t fill_textconv(struct userdiff_driver *driver,
* and only if it has textconv enabled (otherwise return NULL). The result
* can be passed to fill_textconv().
*/
-struct userdiff_driver *get_textconv(struct diff_filespec *one);
+struct userdiff_driver *get_textconv(struct index_state *istate,
+ struct diff_filespec *one);
/*
* Prepare diff_filespec and convert it using diff textconv API
* if the textconv driver exists.
* Return 1 if the conversion succeeds, 0 otherwise.
*/
-int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);
+int textconv_object(struct repository *repo,
+ const char *path,
+ unsigned mode,
+ const struct object_id *oid, int oid_valid,
+ char **buf, unsigned long *buf_size);
int parse_rename_score(const char **cp_p);
diff --git a/diffcore-break.c b/diffcore-break.c
index e11fcfdb39..875aefd3fe 100644
--- a/diffcore-break.c
+++ b/diffcore-break.c
@@ -5,7 +5,8 @@
#include "diff.h"
#include "diffcore.h"
-static int should_break(struct diff_filespec *src,
+static int should_break(struct repository *r,
+ struct diff_filespec *src,
struct diff_filespec *dst,
int break_score,
int *merge_score_p)
@@ -61,7 +62,8 @@ static int should_break(struct diff_filespec *src,
oideq(&src->oid, &dst->oid))
return 0; /* they are the same */
- if (diff_populate_filespec(src, 0) || diff_populate_filespec(dst, 0))
+ if (diff_populate_filespec(r, src, 0) ||
+ diff_populate_filespec(r, dst, 0))
return 0; /* error but caught downstream */
max_size = ((src->size > dst->size) ? src->size : dst->size);
@@ -71,7 +73,7 @@ static int should_break(struct diff_filespec *src,
if (!src->size)
return 0; /* we do not let empty files get renamed */
- if (diffcore_count_changes(src, dst,
+ if (diffcore_count_changes(r, src, dst,
&src->cnt_data, &dst->cnt_data,
&src_copied, &literal_added))
return 0;
@@ -114,7 +116,7 @@ static int should_break(struct diff_filespec *src,
return 1;
}
-void diffcore_break(int break_score)
+void diffcore_break(struct repository *r, int break_score)
{
struct diff_queue_struct *q = &diff_queued_diff;
struct diff_queue_struct outq;
@@ -178,7 +180,7 @@ void diffcore_break(int break_score)
object_type(p->one->mode) == OBJ_BLOB &&
object_type(p->two->mode) == OBJ_BLOB &&
!strcmp(p->one->path, p->two->path)) {
- if (should_break(p->one, p->two,
+ if (should_break(r, p->one, p->two,
break_score, &score)) {
/* Split this into delete and create */
struct diff_filespec *null_one, *null_two;
diff --git a/diffcore-delta.c b/diffcore-delta.c
index c83d45a047..5668ace60d 100644
--- a/diffcore-delta.c
+++ b/diffcore-delta.c
@@ -121,14 +121,15 @@ static int spanhash_cmp(const void *a_, const void *b_)
a->hashval > b->hashval ? 1 : 0;
}
-static struct spanhash_top *hash_chars(struct diff_filespec *one)
+static struct spanhash_top *hash_chars(struct repository *r,
+ struct diff_filespec *one)
{
int i, n;
unsigned int accum1, accum2, hashval;
struct spanhash_top *hash;
unsigned char *buf = one->data;
unsigned int sz = one->size;
- int is_text = !diff_filespec_is_binary(one);
+ int is_text = !diff_filespec_is_binary(r, one);
i = INITIAL_HASH_SIZE;
hash = xmalloc(st_add(sizeof(*hash),
@@ -162,7 +163,8 @@ static struct spanhash_top *hash_chars(struct diff_filespec *one)
return hash;
}
-int diffcore_count_changes(struct diff_filespec *src,
+int diffcore_count_changes(struct repository *r,
+ struct diff_filespec *src,
struct diff_filespec *dst,
void **src_count_p,
void **dst_count_p,
@@ -177,14 +179,14 @@ int diffcore_count_changes(struct diff_filespec *src,
if (src_count_p)
src_count = *src_count_p;
if (!src_count) {
- src_count = hash_chars(src);
+ src_count = hash_chars(r, src);
if (src_count_p)
*src_count_p = src_count;
}
if (dst_count_p)
dst_count = *dst_count_p;
if (!dst_count) {
- dst_count = hash_chars(dst);
+ dst_count = hash_chars(r, dst);
if (dst_count_p)
*dst_count_p = dst_count;
}
diff --git a/diffcore-pickaxe.c b/diffcore-pickaxe.c
index 800a899c86..d2361e06a1 100644
--- a/diffcore-pickaxe.c
+++ b/diffcore-pickaxe.c
@@ -139,8 +139,8 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
return 0;
if (o->flags.allow_textconv) {
- textconv_one = get_textconv(p->one);
- textconv_two = get_textconv(p->two);
+ textconv_one = get_textconv(o->repo->index, p->one);
+ textconv_two = get_textconv(o->repo->index, p->two);
}
/*
@@ -153,8 +153,8 @@ static int pickaxe_match(struct diff_filepair *p, struct diff_options *o,
if (textconv_one == textconv_two && diff_unmodified_pair(p))
return 0;
- mf1.size = fill_textconv(textconv_one, p->one, &mf1.ptr);
- mf2.size = fill_textconv(textconv_two, p->two, &mf2.ptr);
+ mf1.size = fill_textconv(o->repo, textconv_one, p->one, &mf1.ptr);
+ mf2.size = fill_textconv(o->repo, textconv_two, p->two, &mf2.ptr);
ret = fn(DIFF_FILE_VALID(p->one) ? &mf1 : NULL,
DIFF_FILE_VALID(p->two) ? &mf2 : NULL,
diff --git a/diffcore-rename.c b/diffcore-rename.c
index daddd9b28a..07bd34b631 100644
--- a/diffcore-rename.c
+++ b/diffcore-rename.c
@@ -128,7 +128,8 @@ struct diff_score {
short name_score;
};
-static int estimate_similarity(struct diff_filespec *src,
+static int estimate_similarity(struct repository *r,
+ struct diff_filespec *src,
struct diff_filespec *dst,
int minimum_score)
{
@@ -165,10 +166,10 @@ static int estimate_similarity(struct diff_filespec *src,
* say whether the size is valid or not!)
*/
if (!src->cnt_data &&
- diff_populate_filespec(src, CHECK_SIZE_ONLY))
+ diff_populate_filespec(r, src, CHECK_SIZE_ONLY))
return 0;
if (!dst->cnt_data &&
- diff_populate_filespec(dst, CHECK_SIZE_ONLY))
+ diff_populate_filespec(r, dst, CHECK_SIZE_ONLY))
return 0;
max_size = ((src->size > dst->size) ? src->size : dst->size);
@@ -186,12 +187,12 @@ static int estimate_similarity(struct diff_filespec *src,
if (max_size * (MAX_SCORE-minimum_score) < delta_size * MAX_SCORE)
return 0;
- if (!src->cnt_data && diff_populate_filespec(src, 0))
+ if (!src->cnt_data && diff_populate_filespec(r, src, 0))
return 0;
- if (!dst->cnt_data && diff_populate_filespec(dst, 0))
+ if (!dst->cnt_data && diff_populate_filespec(r, dst, 0))
return 0;
- if (diffcore_count_changes(src, dst,
+ if (diffcore_count_changes(r, src, dst,
&src->cnt_data, &dst->cnt_data,
&src_copied, &literal_added))
return 0;
@@ -256,10 +257,11 @@ struct file_similarity {
struct diff_filespec *filespec;
};
-static unsigned int hash_filespec(struct diff_filespec *filespec)
+static unsigned int hash_filespec(struct repository *r,
+ struct diff_filespec *filespec)
{
if (!filespec->oid_valid) {
- if (diff_populate_filespec(filespec, 0))
+ if (diff_populate_filespec(r, filespec, 0))
return 0;
hash_object_file(filespec->data, filespec->size, "blob",
&filespec->oid);
@@ -280,7 +282,9 @@ static int find_identical_files(struct hashmap *srcs,
/*
* Find the best source match for specified destination.
*/
- p = hashmap_get_from_hash(srcs, hash_filespec(target), NULL);
+ p = hashmap_get_from_hash(srcs,
+ hash_filespec(options->repo, target),
+ NULL);
for (; p; p = hashmap_get_next(srcs, p)) {
int score;
struct diff_filespec *source = p->filespec;
@@ -316,14 +320,16 @@ static int find_identical_files(struct hashmap *srcs,
return renames;
}
-static void insert_file_table(struct hashmap *table, int index, struct diff_filespec *filespec)
+static void insert_file_table(struct repository *r,
+ struct hashmap *table, int index,
+ struct diff_filespec *filespec)
{
struct file_similarity *entry = xmalloc(sizeof(*entry));
entry->index = index;
entry->filespec = filespec;
- hashmap_entry_init(entry, hash_filespec(filespec));
+ hashmap_entry_init(entry, hash_filespec(r, filespec));
hashmap_add(table, entry);
}
@@ -344,7 +350,9 @@ static int find_exact_renames(struct diff_options *options)
*/
hashmap_init(&file_table, NULL, NULL, rename_src_nr);
for (i = rename_src_nr-1; i >= 0; i--)
- insert_file_table(&file_table, i, rename_src[i].p->one);
+ insert_file_table(options->repo,
+ &file_table, i,
+ rename_src[i].p->one);
/* Walk the destinations and find best source match */
for (i = 0; i < rename_dst_nr; i++)
@@ -557,7 +565,8 @@ void diffcore_rename(struct diff_options *options)
diff_unmodified_pair(rename_src[j].p))
continue;
- this_src.score = estimate_similarity(one, two,
+ this_src.score = estimate_similarity(options->repo,
+ one, two,
minimum_score);
this_src.name_score = basename_same(one, two);
this_src.dst = i;
diff --git a/diffcore.h b/diffcore.h
index 8d81a45f51..b651061c0e 100644
--- a/diffcore.h
+++ b/diffcore.h
@@ -7,6 +7,8 @@
#include "cache.h"
struct diff_options;
+struct repository;
+struct userdiff_driver;
/* This header file is internal between diff.c and its diff transformers
* (e.g. diffcore-rename, diffcore-pickaxe). Never include this header
@@ -26,8 +28,6 @@ struct diff_options;
#define MINIMUM_BREAK_SIZE 400 /* do not break a file smaller than this */
-struct userdiff_driver;
-
struct diff_filespec {
struct object_id oid;
char *path;
@@ -61,10 +61,10 @@ void fill_filespec(struct diff_filespec *, const struct object_id *,
#define CHECK_SIZE_ONLY 1
#define CHECK_BINARY 2
-int diff_populate_filespec(struct diff_filespec *, unsigned int);
+int diff_populate_filespec(struct repository *, struct diff_filespec *, unsigned int);
void diff_free_filespec_data(struct diff_filespec *);
void diff_free_filespec_blob(struct diff_filespec *);
-int diff_filespec_is_binary(struct diff_filespec *);
+int diff_filespec_is_binary(struct repository *, struct diff_filespec *);
struct diff_filepair {
struct diff_filespec *one;
@@ -111,7 +111,7 @@ struct diff_filepair *diff_queue(struct diff_queue_struct *,
struct diff_filespec *);
void diff_q(struct diff_queue_struct *, struct diff_filepair *);
-void diffcore_break(int);
+void diffcore_break(struct repository *, int);
void diffcore_rename(struct diff_options *);
void diffcore_merge_broken(void);
void diffcore_pickaxe(struct diff_options *);
@@ -142,7 +142,8 @@ void diff_debug_queue(const char *, struct diff_queue_struct *);
#define diff_debug_queue(a,b) do { /* nothing */ } while (0)
#endif
-int diffcore_count_changes(struct diff_filespec *src,
+int diffcore_count_changes(struct repository *r,
+ struct diff_filespec *src,
struct diff_filespec *dst,
void **src_count_p,
void **dst_count_p,
diff --git a/ewah/ewok_rlw.h b/ewah/ewok_rlw.h
index 7cdfdd0c02..d487966935 100644
--- a/ewah/ewok_rlw.h
+++ b/ewah/ewok_rlw.h
@@ -19,6 +19,8 @@
#ifndef __EWOK_RLW_H__
#define __EWOK_RLW_H__
+#include "ewok.h"
+
#define RLW_RUNNING_BITS (sizeof(eword_t) * 4)
#define RLW_LITERAL_BITS (sizeof(eword_t) * 8 - 1 - RLW_RUNNING_BITS)
diff --git a/fetch-pack.c b/fetch-pack.c
index 75047a4b2a..b3ed7121bc 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -76,8 +76,7 @@ struct alternate_object_cache {
size_t nr, alloc;
};
-static void cache_one_alternate(const char *refname,
- const struct object_id *oid,
+static void cache_one_alternate(const struct object_id *oid,
void *vcache)
{
struct alternate_object_cache *cache = vcache;
@@ -253,8 +252,10 @@ static int find_common(struct fetch_negotiator *negotiator,
if (args->stateless_rpc && multi_ack == 1)
die(_("--stateless-rpc requires multi_ack_detailed"));
- mark_tips(negotiator, args->negotiation_tips);
- for_each_cached_alternate(negotiator, insert_one_alternate_object);
+ if (!args->no_dependents) {
+ mark_tips(negotiator, args->negotiation_tips);
+ for_each_cached_alternate(negotiator, insert_one_alternate_object);
+ }
fetching = 0;
for ( ; refs ; refs = refs->next) {
@@ -271,8 +272,12 @@ static int find_common(struct fetch_negotiator *negotiator,
* We use lookup_object here because we are only
* interested in the case we *know* the object is
* reachable and we have already scanned it.
+ *
+ * Do this only if args->no_dependents is false (if it is true,
+ * we cannot trust the object flags).
*/
- if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+ if (!args->no_dependents &&
+ ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
@@ -526,21 +531,14 @@ static void add_refs_to_oidset(struct oidset *oids, struct ref *refs)
oidset_insert(oids, &refs->old_oid);
}
-static int tip_oids_contain(struct oidset *tip_oids,
- struct ref *unmatched, struct ref *newlist,
- const struct object_id *id)
+static int is_unmatched_ref(const struct ref *ref)
{
- /*
- * Note that this only looks at the ref lists the first time it's
- * called. This works out in filter_refs() because even though it may
- * add to "newlist" between calls, the additions will always be for
- * oids that are already in the set.
- */
- if (!tip_oids->map.map.tablesize) {
- add_refs_to_oidset(tip_oids, unmatched);
- add_refs_to_oidset(tip_oids, newlist);
- }
- return oidset_contains(tip_oids, id);
+ struct object_id oid;
+ const char *p;
+ return ref->match_status == REF_NOT_MATCHED &&
+ !parse_oid_hex(ref->name, &oid, &p) &&
+ *p == '\0' &&
+ oideq(&oid, &ref->old_oid);
}
static void filter_refs(struct fetch_pack_args *args,
@@ -553,6 +551,8 @@ static void filter_refs(struct fetch_pack_args *args,
struct ref *ref, *next;
struct oidset tip_oids = OIDSET_INIT;
int i;
+ int strict = !(allow_unadvertised_object_request &
+ (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1));
i = 0;
for (ref = *refs; ref; ref = next) {
@@ -589,23 +589,25 @@ static void filter_refs(struct fetch_pack_args *args,
}
}
+ if (strict) {
+ for (i = 0; i < nr_sought; i++) {
+ ref = sought[i];
+ if (!is_unmatched_ref(ref))
+ continue;
+
+ add_refs_to_oidset(&tip_oids, unmatched);
+ add_refs_to_oidset(&tip_oids, newlist);
+ break;
+ }
+ }
+
/* Append unmatched requests to the list */
for (i = 0; i < nr_sought; i++) {
- struct object_id oid;
- const char *p;
-
ref = sought[i];
- if (ref->match_status != REF_NOT_MATCHED)
- continue;
- if (parse_oid_hex(ref->name, &oid, &p) ||
- *p != '\0' ||
- !oideq(&oid, &ref->old_oid))
+ if (!is_unmatched_ref(ref))
continue;
- if ((allow_unadvertised_object_request &
- (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)) ||
- tip_oids_contain(&tip_oids, unmatched, newlist,
- &ref->old_oid)) {
+ if (!strict || oidset_contains(&tip_oids, &ref->old_oid)) {
ref->match_status = REF_MATCHED;
*newtail = copy_ref(ref);
newtail = &(*newtail)->next;
@@ -710,31 +712,29 @@ static void mark_complete_and_common_ref(struct fetch_negotiator *negotiator,
oidset_clear(&loose_oid_set);
- if (!args->no_dependents) {
- if (!args->deepen) {
- for_each_ref(mark_complete_oid, NULL);
- for_each_cached_alternate(NULL, mark_alternate_complete);
- commit_list_sort_by_date(&complete);
- if (cutoff)
- mark_recent_complete_commits(args, cutoff);
- }
+ if (!args->deepen) {
+ for_each_ref(mark_complete_oid, NULL);
+ for_each_cached_alternate(NULL, mark_alternate_complete);
+ commit_list_sort_by_date(&complete);
+ if (cutoff)
+ mark_recent_complete_commits(args, cutoff);
+ }
- /*
- * Mark all complete remote refs as common refs.
- * Don't mark them common yet; the server has to be told so first.
- */
- for (ref = *refs; ref; ref = ref->next) {
- struct object *o = deref_tag(the_repository,
- lookup_object(the_repository,
- ref->old_oid.hash),
- NULL, 0);
+ /*
+ * Mark all complete remote refs as common refs.
+ * Don't mark them common yet; the server has to be told so first.
+ */
+ for (ref = *refs; ref; ref = ref->next) {
+ struct object *o = deref_tag(the_repository,
+ lookup_object(the_repository,
+ ref->old_oid.hash),
+ NULL, 0);
- if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
- continue;
+ if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
+ continue;
- negotiator->known_common(negotiator,
- (struct commit *)o);
- }
+ negotiator->known_common(negotiator,
+ (struct commit *)o);
}
save_commit_buffer = old_save_commit_buffer;
@@ -990,11 +990,15 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
if (!server_supports("deepen-relative") && args->deepen_relative)
die(_("Server does not support --deepen"));
- mark_complete_and_common_ref(&negotiator, args, &ref);
- filter_refs(args, &ref, sought, nr_sought);
- if (everything_local(args, &ref)) {
- packet_flush(fd[1]);
- goto all_done;
+ if (!args->no_dependents) {
+ mark_complete_and_common_ref(&negotiator, args, &ref);
+ filter_refs(args, &ref, sought, nr_sought);
+ if (everything_local(args, &ref)) {
+ packet_flush(fd[1]);
+ goto all_done;
+ }
+ } else {
+ filter_refs(args, &ref, sought, nr_sought);
}
if (find_common(&negotiator, args, fd, &oid, ref) < 0)
if (!args->keep_pack)
@@ -1040,7 +1044,7 @@ static void add_shallow_requests(struct strbuf *req_buf,
}
}
-static void add_wants(const struct ref *wants, struct strbuf *req_buf)
+static void add_wants(int no_dependents, const struct ref *wants, struct strbuf *req_buf)
{
int use_ref_in_want = server_supports_feature("fetch", "ref-in-want", 0);
@@ -1057,8 +1061,12 @@ static void add_wants(const struct ref *wants, struct strbuf *req_buf)
* We use lookup_object here because we are only
* interested in the case we *know* the object is
* reachable and we have already scanned it.
+ *
+ * Do this only if args->no_dependents is false (if it is true,
+ * we cannot trust the object flags).
*/
- if (((o = lookup_object(the_repository, remote->hash)) != NULL) &&
+ if (!no_dependents &&
+ ((o = lookup_object(the_repository, remote->hash)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
@@ -1155,7 +1163,7 @@ static int send_fetch_request(struct fetch_negotiator *negotiator, int fd_out,
}
/* add wants */
- add_wants(wants, &req_buf);
+ add_wants(args->no_dependents, wants, &req_buf);
if (args->no_dependents) {
packet_buf_write(&req_buf, "done");
@@ -1346,16 +1354,21 @@ static struct ref *do_fetch_pack_v2(struct fetch_pack_args *args,
args->deepen = 1;
/* Filter 'ref' by 'sought' and those that aren't local */
- mark_complete_and_common_ref(&negotiator, args, &ref);
- filter_refs(args, &ref, sought, nr_sought);
- if (everything_local(args, &ref))
- state = FETCH_DONE;
- else
+ if (!args->no_dependents) {
+ mark_complete_and_common_ref(&negotiator, args, &ref);
+ filter_refs(args, &ref, sought, nr_sought);
+ if (everything_local(args, &ref))
+ state = FETCH_DONE;
+ else
+ state = FETCH_SEND_REQUEST;
+
+ mark_tips(&negotiator, args->negotiation_tips);
+ for_each_cached_alternate(&negotiator,
+ insert_one_alternate_object);
+ } else {
+ filter_refs(args, &ref, sought, nr_sought);
state = FETCH_SEND_REQUEST;
-
- mark_tips(&negotiator, args->negotiation_tips);
- for_each_cached_alternate(&negotiator,
- insert_one_alternate_object);
+ }
break;
case FETCH_SEND_REQUEST:
if (send_fetch_request(&negotiator, fd[1], args, ref,
@@ -1598,7 +1611,21 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
if (nr_sought)
nr_sought = remove_duplicates_in_refs(sought, nr_sought);
- if (!ref) {
+ if (args->no_dependents && !args->filter_options.choice) {
+ /*
+ * The protocol does not support requesting that only the
+ * wanted objects be sent, so approximate this by setting a
+ * "blob:none" filter if no filter is already set. This works
+ * for all object types: note that wanted blobs will still be
+ * sent because they are directly specified as a "want".
+ *
+ * NEEDSWORK: Add an option in the protocol to request that
+ * only the wanted objects be sent, and implement it.
+ */
+ parse_list_objects_filter(&args->filter_options, "blob:none");
+ }
+
+ if (version != protocol_v2 && !ref) {
packet_flush(fd[1]);
die(_("no matching remote head"));
}
diff --git a/fetch-pack.h b/fetch-pack.h
index 5b6e868802..43ec344d95 100644
--- a/fetch-pack.h
+++ b/fetch-pack.h
@@ -43,6 +43,13 @@ struct fetch_pack_args {
unsigned from_promisor:1;
/*
+ * Attempt to fetch only the wanted objects, and not any objects
+ * referred to by them. Due to protocol limitations, extraneous
+ * objects may still be included. (When fetching non-blob
+ * objects, only blobs are excluded; when fetching a blob, the
+ * blob itself will still be sent. The client does not need to
+ * know whether a wanted object is a blob or not.)
+ *
* If 1, fetch_pack() will also not modify any object flags.
* This allows fetch_pack() to safely be called by any function,
* regardless of which object flags it uses (if any).
diff --git a/fuzz-pack-headers.c b/fuzz-pack-headers.c
new file mode 100644
index 0000000000..99da1d0fd3
--- /dev/null
+++ b/fuzz-pack-headers.c
@@ -0,0 +1,14 @@
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ enum object_type type;
+ unsigned long len;
+
+ unpack_object_header_buffer((const unsigned char *)data,
+ (unsigned long)size, &type, &len);
+
+ return 0;
+}
diff --git a/fuzz-pack-idx.c b/fuzz-pack-idx.c
new file mode 100644
index 0000000000..0c3d777aac
--- /dev/null
+++ b/fuzz-pack-idx.c
@@ -0,0 +1,13 @@
+#include "object-store.h"
+#include "packfile.h"
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size);
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
+{
+ struct packed_git p;
+
+ load_idx("fuzz-input", GIT_SHA1_RAWSZ, (void *)data, size, &p);
+
+ return 0;
+}
diff --git a/git-compat-util.h b/git-compat-util.h
index 5f2e90932f..48c955541e 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -146,8 +146,8 @@
#define _SGI_SOURCE 1
#if defined(WIN32) && !defined(__CYGWIN__) /* Both MinGW and MSVC */
-# if defined (_MSC_VER) && !defined(_WIN32_WINNT)
-# define _WIN32_WINNT 0x0502
+# if !defined(_WIN32_WINNT)
+# define _WIN32_WINNT 0x0600
# endif
#define WIN32_LEAN_AND_MEAN /* stops windows.h including winsock.h */
#include <winsock2.h>
diff --git a/git-rebase.sh b/git-rebase.sh
index 7973447645..45b6ee9c0e 100755
--- a/git-rebase.sh
+++ b/git-rebase.sh
@@ -553,15 +553,15 @@ then
# Note: incompatibility with --interactive is just a strong warning;
# git-rebase.txt caveats with "unless you know what you are doing"
test -n "$rebase_merges" &&
- die "$(gettext "error: cannot combine '--preserve_merges' with '--rebase-merges'")"
+ die "$(gettext "error: cannot combine '--preserve-merges' with '--rebase-merges'")"
fi
if test -n "$rebase_merges"
then
test -n "$strategy_opts" &&
- die "$(gettext "error: cannot combine '--rebase_merges' with '--strategy-option'")"
+ die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy-option'")"
test -n "$strategy" &&
- die "$(gettext "error: cannot combine '--rebase_merges' with '--strategy'")"
+ die "$(gettext "error: cannot combine '--rebase-merges' with '--strategy'")"
fi
if test -z "$rebase_root"
diff --git a/git.c b/git.c
index a6f4b44af5..adac132956 100644
--- a/git.c
+++ b/git.c
@@ -318,6 +318,9 @@ static int handle_alias(int *argcp, const char ***argv)
alias_command = (*argv)[0];
alias_string = alias_lookup(alias_command);
if (alias_string) {
+ if (*argcp > 1 && !strcmp((*argv)[1], "-h"))
+ fprintf_ln(stderr, _("'%s' is aliased to '%s'"),
+ alias_command, alias_string);
if (alias_string[0] == '!') {
struct child_process child = CHILD_PROCESS_INIT;
int nongit_ok;
@@ -675,6 +678,8 @@ static void execv_dashed_external(const char **argv)
static int run_argv(int *argcp, const char ***argv)
{
int done_alias = 0;
+ struct string_list cmd_list = STRING_LIST_INIT_NODUP;
+ struct string_list_item *seen;
while (1) {
/*
@@ -692,17 +697,37 @@ static int run_argv(int *argcp, const char ***argv)
/* .. then try the external ones */
execv_dashed_external(*argv);
- /* It could be an alias -- this works around the insanity
+ seen = unsorted_string_list_lookup(&cmd_list, *argv[0]);
+ if (seen) {
+ int i;
+ struct strbuf sb = STRBUF_INIT;
+ for (i = 0; i < cmd_list.nr; i++) {
+ struct string_list_item *item = &cmd_list.items[i];
+
+ strbuf_addf(&sb, "\n %s", item->string);
+ if (item == seen)
+ strbuf_addstr(&sb, " <==");
+ else if (i == cmd_list.nr - 1)
+ strbuf_addstr(&sb, " ==>");
+ }
+ die(_("alias loop detected: expansion of '%s' does"
+ " not terminate:%s"), cmd_list.items[0].string, sb.buf);
+ }
+
+ string_list_append(&cmd_list, *argv[0]);
+
+ /*
+ * It could be an alias -- this works around the insanity
* of overriding "git log" with "git show" by having
* alias.log = show
*/
- if (done_alias)
- break;
if (!handle_alias(argcp, argv))
break;
done_alias = 1;
}
+ string_list_clear(&cmd_list, 0);
+
return done_alias;
}
diff --git a/graph.c b/graph.c
index e1f6d3bddb..f53135485f 100644
--- a/graph.c
+++ b/graph.c
@@ -842,27 +842,55 @@ static void graph_output_commit_char(struct git_graph *graph, struct strbuf *sb)
}
/*
- * Draw an octopus merge and return the number of characters written.
+ * Draw the horizontal dashes of an octopus merge and return the number of
+ * characters written.
*/
static int graph_draw_octopus_merge(struct git_graph *graph,
struct strbuf *sb)
{
/*
- * Here dashless_commits represents the number of parents
- * which don't need to have dashes (because their edges fit
- * neatly under the commit).
- */
- const int dashless_commits = 2;
- int col_num, i;
- int num_dashes =
- ((graph->num_parents - dashless_commits) * 2) - 1;
- for (i = 0; i < num_dashes; i++) {
- col_num = (i / 2) + dashless_commits + graph->commit_index;
- strbuf_write_column(sb, &graph->new_columns[col_num], '-');
+ * Here dashless_parents represents the number of parents which don't
+ * need to have dashes (the edges labeled "0" and "1"). And
+ * dashful_parents are the remaining ones.
+ *
+ * | *---.
+ * | |\ \ \
+ * | | | | |
+ * x 0 1 2 3
+ *
+ */
+ const int dashless_parents = 2;
+ int dashful_parents = graph->num_parents - dashless_parents;
+
+ /*
+ * Usually, we add one new column for each parent (like the diagram
+ * above) but sometimes the first parent goes into an existing column,
+ * like this:
+ *
+ * | *---.
+ * | |\ \ \
+ * |/ / / /
+ * x 0 1 2
+ *
+ * In which case the number of parents will be one greater than the
+ * number of added columns.
+ */
+ int added_cols = (graph->num_new_columns - graph->num_columns);
+ int parent_in_old_cols = graph->num_parents - added_cols;
+
+ /*
+ * In both cases, commit_index corresponds to the edge labeled "0".
+ */
+ int first_col = graph->commit_index + dashless_parents
+ - parent_in_old_cols;
+
+ int i;
+ for (i = 0; i < dashful_parents; i++) {
+ strbuf_write_column(sb, &graph->new_columns[i+first_col], '-');
+ strbuf_write_column(sb, &graph->new_columns[i+first_col],
+ i == dashful_parents-1 ? '.' : '-');
}
- col_num = (i / 2) + dashless_commits + graph->commit_index;
- strbuf_write_column(sb, &graph->new_columns[col_num], '.');
- return num_dashes + 1;
+ return 2 * dashful_parents;
}
static void graph_output_commit_line(struct git_graph *graph, struct strbuf *sb)
diff --git a/grep.c b/grep.c
index 2b26cee08d..f6bd89e40b 100644
--- a/grep.c
+++ b/grep.c
@@ -11,7 +11,8 @@
#include "help.h"
static int grep_source_load(struct grep_source *gs);
-static int grep_source_is_binary(struct grep_source *gs);
+static int grep_source_is_binary(struct grep_source *gs,
+ struct index_state *istate);
static struct grep_opt grep_defaults;
@@ -42,7 +43,7 @@ static void color_set(char *dst, const char *color_bytes)
* We could let the compiler do this, but without C99 initializers
* the code gets unwieldy and unreadable, so...
*/
-void init_grep_defaults(void)
+void init_grep_defaults(struct repository *repo)
{
struct grep_opt *opt = &grep_defaults;
static int run_once;
@@ -52,6 +53,7 @@ void init_grep_defaults(void)
run_once++;
memset(opt, 0, sizeof(*opt));
+ opt->repo = repo;
opt->relative = 1;
opt->pathname = 1;
opt->max_depth = -1;
@@ -149,12 +151,13 @@ int grep_config(const char *var, const char *value, void *cb)
* default values from the template we read the configuration
* information in an earlier call to git_config(grep_config).
*/
-void grep_init(struct grep_opt *opt, const char *prefix)
+void grep_init(struct grep_opt *opt, struct repository *repo, const char *prefix)
{
struct grep_opt *def = &grep_defaults;
int i;
memset(opt, 0, sizeof(*opt));
+ opt->repo = repo;
opt->prefix = prefix;
opt->prefix_length = (prefix && *prefix) ? strlen(prefix) : 0;
opt->pattern_tail = &opt->pattern_list;
@@ -1545,7 +1548,7 @@ static int match_funcname(struct grep_opt *opt, struct grep_source *gs, char *bo
{
xdemitconf_t *xecfg = opt->priv;
if (xecfg && !xecfg->find_func) {
- grep_source_load_driver(gs);
+ grep_source_load_driver(gs, opt->repo->index);
if (gs->driver->funcname.pattern) {
const struct userdiff_funcname *pe = &gs->driver->funcname;
xdiff_set_find_func(xecfg, pe->pattern, pe->cflags);
@@ -1708,7 +1711,8 @@ static int look_ahead(struct grep_opt *opt,
return 0;
}
-static int fill_textconv_grep(struct userdiff_driver *driver,
+static int fill_textconv_grep(struct repository *r,
+ struct userdiff_driver *driver,
struct grep_source *gs)
{
struct diff_filespec *df;
@@ -1741,7 +1745,7 @@ static int fill_textconv_grep(struct userdiff_driver *driver,
* structure.
*/
grep_read_lock();
- size = fill_textconv(driver, df, &buf);
+ size = fill_textconv(r, driver, df, &buf);
grep_read_unlock();
free_filespec(df);
@@ -1801,7 +1805,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
opt->last_shown = 0;
if (opt->allow_textconv) {
- grep_source_load_driver(gs);
+ grep_source_load_driver(gs, opt->repo->index);
/*
* We might set up the shared textconv cache data here, which
* is not thread-safe.
@@ -1818,11 +1822,11 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
if (!textconv) {
switch (opt->binary) {
case GREP_BINARY_DEFAULT:
- if (grep_source_is_binary(gs))
+ if (grep_source_is_binary(gs, opt->repo->index))
binary_match_only = 1;
break;
case GREP_BINARY_NOMATCH:
- if (grep_source_is_binary(gs))
+ if (grep_source_is_binary(gs, opt->repo->index))
return 0; /* Assume unmatch */
break;
case GREP_BINARY_TEXT:
@@ -1837,7 +1841,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
try_lookahead = should_lookahead(opt);
- if (fill_textconv_grep(textconv, gs) < 0)
+ if (fill_textconv_grep(opt->repo, textconv, gs) < 0)
return 0;
bol = gs->buf;
@@ -2168,22 +2172,24 @@ static int grep_source_load(struct grep_source *gs)
BUG("invalid grep_source type to load");
}
-void grep_source_load_driver(struct grep_source *gs)
+void grep_source_load_driver(struct grep_source *gs,
+ struct index_state *istate)
{
if (gs->driver)
return;
grep_attr_lock();
if (gs->path)
- gs->driver = userdiff_find_by_path(gs->path);
+ gs->driver = userdiff_find_by_path(istate, gs->path);
if (!gs->driver)
gs->driver = userdiff_find_by_name("default");
grep_attr_unlock();
}
-static int grep_source_is_binary(struct grep_source *gs)
+static int grep_source_is_binary(struct grep_source *gs,
+ struct index_state *istate)
{
- grep_source_load_driver(gs);
+ grep_source_load_driver(gs, istate);
if (gs->driver->binary != -1)
return gs->driver->binary;
diff --git a/grep.h b/grep.h
index 0ba62a11c5..1a57d12b90 100644
--- a/grep.h
+++ b/grep.h
@@ -36,6 +36,8 @@ typedef int pcre2_jit_stack;
#include "thread-utils.h"
#include "userdiff.h"
+struct repository;
+
enum grep_pat_token {
GREP_PATTERN,
GREP_PATTERN_HEAD,
@@ -136,6 +138,7 @@ struct grep_opt {
struct grep_pat *header_list;
struct grep_pat **header_tail;
struct grep_expr *pattern_expression;
+ struct repository *repo;
const char *prefix;
int prefix_length;
regex_t regexp;
@@ -183,9 +186,9 @@ struct grep_opt {
void *output_priv;
};
-extern void init_grep_defaults(void);
+extern void init_grep_defaults(struct repository *);
extern int grep_config(const char *var, const char *value, void *);
-extern void grep_init(struct grep_opt *, const char *prefix);
+extern void grep_init(struct grep_opt *, struct repository *repo, const char *prefix);
void grep_commit_pattern_type(enum grep_pattern_type, struct grep_opt *opt);
extern void append_grep_pat(struct grep_opt *opt, const char *pat, size_t patlen, const char *origin, int no, enum grep_pat_token t);
@@ -217,7 +220,8 @@ void grep_source_init(struct grep_source *gs, enum grep_source_type type,
const void *identifier);
void grep_source_clear_data(struct grep_source *gs);
void grep_source_clear(struct grep_source *gs);
-void grep_source_load_driver(struct grep_source *gs);
+void grep_source_load_driver(struct grep_source *gs,
+ struct index_state *istate);
int grep_source(struct grep_opt *opt, struct grep_source *gs);
diff --git a/help.c b/help.c
index 96f6d221ed..4745b32299 100644
--- a/help.c
+++ b/help.c
@@ -98,7 +98,8 @@ static int cmd_name_cmp(const void *elem1, const void *elem2)
return strcmp(e1->name, e2->name);
}
-static void print_cmd_by_category(const struct category_description *catdesc)
+static void print_cmd_by_category(const struct category_description *catdesc,
+ int *longest_p)
{
struct cmdname_help *cmds;
int longest = 0;
@@ -124,6 +125,8 @@ static void print_cmd_by_category(const struct category_description *catdesc)
print_command_list(cmds, mask, longest);
}
free(cmds);
+ if (longest_p)
+ *longest_p = longest;
}
void add_cmdname(struct cmdnames *cmds, const char *name, int len)
@@ -307,7 +310,7 @@ void list_commands(unsigned int colopts,
void list_common_cmds_help(void)
{
puts(_("These are common Git commands used in various situations:"));
- print_cmd_by_category(common_categories);
+ print_cmd_by_category(common_categories, NULL);
}
void list_all_main_cmds(struct string_list *list)
@@ -405,7 +408,7 @@ void list_common_guides_help(void)
{ CAT_guide, N_("The common Git guides are:") },
{ 0, NULL }
};
- print_cmd_by_category(catdesc);
+ print_cmd_by_category(catdesc, NULL);
putchar('\n');
}
@@ -494,9 +497,48 @@ void list_config_help(int for_human)
string_list_clear(&keys, 0);
}
+static int get_alias(const char *var, const char *value, void *data)
+{
+ struct string_list *list = data;
+
+ if (skip_prefix(var, "alias.", &var))
+ string_list_append(list, var)->util = xstrdup(value);
+
+ return 0;
+}
+
void list_all_cmds_help(void)
{
- print_cmd_by_category(main_categories);
+ struct string_list others = STRING_LIST_INIT_DUP;
+ struct string_list alias_list = STRING_LIST_INIT_DUP;
+ struct cmdname_help *aliases;
+ int i, longest;
+
+ printf_ln(_("See 'git help <command>' to read about a specific subcommand"));
+ print_cmd_by_category(main_categories, &longest);
+
+ list_all_other_cmds(&others);
+ if (others.nr)
+ printf("\n%s\n", _("External commands"));
+ for (i = 0; i < others.nr; i++)
+ printf(" %s\n", others.items[i].string);
+ string_list_clear(&others, 0);
+
+ git_config(get_alias, &alias_list);
+ string_list_sort(&alias_list);
+ if (alias_list.nr) {
+ printf("\n%s\n", _("Command aliases"));
+ ALLOC_ARRAY(aliases, alias_list.nr + 1);
+ for (i = 0; i < alias_list.nr; i++) {
+ aliases[i].name = alias_list.items[i].string;
+ aliases[i].help = alias_list.items[i].util;
+ aliases[i].category = 1;
+ }
+ aliases[alias_list.nr].name = NULL;
+ print_command_list(aliases, 1, longest);
+ free(aliases);
+ }
+ string_list_clear(&alias_list, 1);
}
int is_in_cmdlist(struct cmdnames *c, const char *s)
diff --git a/http-push.c b/http-push.c
index 1bbb0cdb6d..d1f52cbdf6 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1925,7 +1925,7 @@ int cmd_main(int argc, const char **argv)
if (!push_all && !is_null_oid(&ref->old_oid))
argv_array_pushf(&commit_argv, "^%s",
oid_to_hex(&ref->old_oid));
- init_revisions(&revs, setup_git_directory());
+ repo_init_revisions(the_repository, &revs, setup_git_directory());
setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL);
revs.edge_hint = 0; /* just in case */
diff --git a/json-writer.h b/json-writer.h
index fc18acc7d9..83906b09c1 100644
--- a/json-writer.h
+++ b/json-writer.h
@@ -42,6 +42,8 @@
* of the given strings.
*/
+#include "strbuf.h"
+
struct json_writer
{
/*
diff --git a/khash.h b/khash.h
index 07b4cc2e67..d10caa0c35 100644
--- a/khash.h
+++ b/khash.h
@@ -82,11 +82,16 @@ static const double __ac_HASH_UPPER = 0.77;
SCOPE kh_##name##_t *kh_init_##name(void) { \
return (kh_##name##_t*)xcalloc(1, sizeof(kh_##name##_t)); \
} \
+ SCOPE void kh_release_##name(kh_##name##_t *h) \
+ { \
+ free(h->flags); \
+ free((void *)h->keys); \
+ free((void *)h->vals); \
+ } \
SCOPE void kh_destroy_##name(kh_##name##_t *h) \
{ \
if (h) { \
- free((void *)h->keys); free(h->flags); \
- free((void *)h->vals); \
+ kh_release_##name(h); \
free(h); \
} \
} \
diff --git a/line-log.c b/line-log.c
index 72a5fed661..d1d429d738 100644
--- a/line-log.c
+++ b/line-log.c
@@ -508,7 +508,9 @@ static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)
return;
}
-static void fill_line_ends(struct diff_filespec *spec, long *lines,
+static void fill_line_ends(struct repository *r,
+ struct diff_filespec *spec,
+ long *lines,
unsigned long **line_ends)
{
int num = 0, size = 50;
@@ -516,7 +518,7 @@ static void fill_line_ends(struct diff_filespec *spec, long *lines,
unsigned long *ends = NULL;
char *data = NULL;
- if (diff_populate_filespec(spec, 0))
+ if (diff_populate_filespec(r, spec, 0))
die("Cannot read blob %s", oid_to_hex(&spec->oid));
ALLOC_ARRAY(ends, size);
@@ -555,7 +557,8 @@ static const char *nth_line(void *data, long line)
}
static struct line_log_data *
-parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
+parse_lines(struct repository *r, struct commit *commit,
+ const char *prefix, struct string_list *args)
{
long lines = 0;
unsigned long *ends = NULL;
@@ -571,7 +574,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
long begin = 0, end = 0;
long anchor;
- name_part = skip_range_arg(item->string);
+ name_part = skip_range_arg(item->string, r->index);
if (!name_part || *name_part != ':' || !name_part[1])
die("-L argument not 'start,end:file' or ':funcname:file': %s",
item->string);
@@ -583,7 +586,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
spec = alloc_filespec(full_name);
fill_blob_sha1(commit, spec);
- fill_line_ends(spec, &lines, &ends);
+ fill_line_ends(r, spec, &lines, &ends);
cb_data.spec = spec;
cb_data.lines = lines;
cb_data.line_ends = ends;
@@ -596,7 +599,7 @@ parse_lines(struct commit *commit, const char *prefix, struct string_list *args)
if (parse_range_arg(range_part, nth_line, &cb_data,
lines, anchor, &begin, &end,
- full_name))
+ full_name, r->index))
die("malformed -L argument '%s'", range_part);
if ((!lines && (begin || end)) || lines < begin)
die("file %s has only %lu lines", name_part, lines);
@@ -739,7 +742,7 @@ void line_log_init(struct rev_info *rev, const char *prefix, struct string_list
struct line_log_data *range;
commit = check_single_commit(rev);
- range = parse_lines(commit, prefix, args);
+ range = parse_lines(rev->diffopt.repo, commit, prefix, args);
add_line_range(rev, commit, range);
if (!rev->diffopt.detect_rename) {
@@ -891,8 +894,8 @@ static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *rang
return;
if (pair->one->oid_valid)
- fill_line_ends(pair->one, &p_lines, &p_ends);
- fill_line_ends(pair->two, &t_lines, &t_ends);
+ fill_line_ends(rev->diffopt.repo, pair->one, &p_lines, &p_ends);
+ fill_line_ends(rev->diffopt.repo, pair->two, &t_lines, &t_ends);
fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset);
fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta,
@@ -1008,12 +1011,12 @@ static int process_diff_filepair(struct rev_info *rev,
return 0;
assert(pair->two->oid_valid);
- diff_populate_filespec(pair->two, 0);
+ diff_populate_filespec(rev->diffopt.repo, pair->two, 0);
file_target.ptr = pair->two->data;
file_target.size = pair->two->size;
if (pair->one->oid_valid) {
- diff_populate_filespec(pair->one, 0);
+ diff_populate_filespec(rev->diffopt.repo, pair->one, 0);
file_parent.ptr = pair->one->data;
file_parent.size = pair->one->size;
} else {
diff --git a/line-range.c b/line-range.c
index 232c3909ec..9b50583dc0 100644
--- a/line-range.c
+++ b/line-range.c
@@ -163,9 +163,10 @@ static const char *find_funcname_matching_regexp(xdemitconf_t *xecfg, const char
}
}
-static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_cb,
- void *cb_data, long lines, long anchor, long *begin, long *end,
- const char *path)
+static const char *parse_range_funcname(
+ const char *arg, nth_line_fn_t nth_line_cb,
+ void *cb_data, long lines, long anchor, long *begin, long *end,
+ const char *path, struct index_state *istate)
{
char *pattern;
const char *term;
@@ -198,7 +199,7 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
anchor--; /* input is in human terms */
start = nth_line_cb(cb_data, anchor);
- drv = userdiff_find_by_path(path);
+ drv = userdiff_find_by_path(istate, path);
if (drv && drv->funcname.pattern) {
const struct userdiff_funcname *pe = &drv->funcname;
xecfg = xcalloc(1, sizeof(*xecfg));
@@ -244,7 +245,8 @@ static const char *parse_range_funcname(const char *arg, nth_line_fn_t nth_line_
int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
void *cb_data, long lines, long anchor,
- long *begin, long *end, const char *path)
+ long *begin, long *end,
+ const char *path, struct index_state *istate)
{
*begin = *end = 0;
@@ -254,7 +256,9 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
anchor = lines + 1;
if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':')) {
- arg = parse_range_funcname(arg, nth_line_cb, cb_data, lines, anchor, begin, end, path);
+ arg = parse_range_funcname(arg, nth_line_cb, cb_data,
+ lines, anchor, begin, end,
+ path, istate);
if (!arg || *arg)
return -1;
return 0;
@@ -275,10 +279,12 @@ int parse_range_arg(const char *arg, nth_line_fn_t nth_line_cb,
return 0;
}
-const char *skip_range_arg(const char *arg)
+const char *skip_range_arg(const char *arg, struct index_state *istate)
{
if (*arg == ':' || (*arg == '^' && *(arg + 1) == ':'))
- return parse_range_funcname(arg, NULL, NULL, 0, 0, NULL, NULL, NULL);
+ return parse_range_funcname(arg, NULL, NULL,
+ 0, 0, NULL, NULL,
+ NULL, istate);
arg = parse_loc(arg, NULL, NULL, 0, -1, NULL);
diff --git a/line-range.h b/line-range.h
index d3c54e45aa..e69bf7c017 100644
--- a/line-range.h
+++ b/line-range.h
@@ -1,6 +1,8 @@
#ifndef LINE_RANGE_H
#define LINE_RANGE_H
+struct index_state;
+
/*
* Parse one item in an -L begin,end option w.r.t. the notional file
* object 'cb_data' consisting of 'lines' lines.
@@ -23,7 +25,7 @@ int parse_range_arg(const char *arg,
nth_line_fn_t nth_line_cb,
void *cb_data, long lines, long anchor,
long *begin, long *end,
- const char *path);
+ const char *path, struct index_state *istate);
/*
* Scan past a range argument that could be parsed by
@@ -34,6 +36,6 @@ int parse_range_arg(const char *arg,
* NULL in case the argument is obviously malformed.
*/
-const char *skip_range_arg(const char *arg);
+const char *skip_range_arg(const char *arg, struct index_state *istate);
#endif /* LINE_RANGE_H */
diff --git a/list-objects.c b/list-objects.c
index c99c47ac18..0c2989d5ca 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -196,7 +196,7 @@ static void mark_edge_parents_uninteresting(struct commit *commit,
struct commit *parent = parents->item;
if (!(parent->object.flags & UNINTERESTING))
continue;
- mark_tree_uninteresting(get_commit_tree(parent));
+ mark_tree_uninteresting(revs->repo, get_commit_tree(parent));
if (revs->edge_hint && !(parent->object.flags & SHOWN)) {
parent->object.flags |= SHOWN;
show_edge(parent);
@@ -213,7 +213,8 @@ void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
struct commit *commit = list->item;
if (commit->object.flags & UNINTERESTING) {
- mark_tree_uninteresting(get_commit_tree(commit));
+ mark_tree_uninteresting(revs->repo,
+ get_commit_tree(commit));
if (revs->edge_hint_aggressive && !(commit->object.flags & SHOWN)) {
commit->object.flags |= SHOWN;
show_edge(commit);
@@ -228,7 +229,8 @@ void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
struct commit *commit = (struct commit *)obj;
if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
continue;
- mark_tree_uninteresting(get_commit_tree(commit));
+ mark_tree_uninteresting(revs->repo,
+ get_commit_tree(commit));
if (!(obj->flags & SHOWN)) {
obj->flags |= SHOWN;
show_edge(commit);
diff --git a/ll-merge.c b/ll-merge.c
index 1936fee9e1..3c8fb917e9 100644
--- a/ll-merge.c
+++ b/ll-merge.c
@@ -336,10 +336,10 @@ static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr
return &ll_merge_drv[LL_TEXT_MERGE];
}
-static void normalize_file(mmfile_t *mm, const char *path)
+static void normalize_file(mmfile_t *mm, const char *path, struct index_state *istate)
{
struct strbuf strbuf = STRBUF_INIT;
- if (renormalize_buffer(&the_index, path, mm->ptr, mm->size, &strbuf)) {
+ if (renormalize_buffer(istate, path, mm->ptr, mm->size, &strbuf)) {
free(mm->ptr);
mm->size = strbuf.len;
mm->ptr = strbuf_detach(&strbuf, NULL);
@@ -351,6 +351,7 @@ int ll_merge(mmbuffer_t *result_buf,
mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
+ struct index_state *istate,
const struct ll_merge_options *opts)
{
static struct attr_check *check;
@@ -363,15 +364,15 @@ int ll_merge(mmbuffer_t *result_buf,
opts = &default_opts;
if (opts->renormalize) {
- normalize_file(ancestor, path);
- normalize_file(ours, path);
- normalize_file(theirs, path);
+ normalize_file(ancestor, path, istate);
+ normalize_file(ours, path, istate);
+ normalize_file(theirs, path, istate);
}
if (!check)
check = attr_check_initl("merge", "conflict-marker-size", NULL);
- git_check_attr(&the_index, path, check);
+ git_check_attr(istate, path, check);
ll_driver_name = check->items[0].value;
if (check->items[1].value) {
marker_size = atoi(check->items[1].value);
@@ -390,14 +391,14 @@ int ll_merge(mmbuffer_t *result_buf,
opts, marker_size);
}
-int ll_merge_marker_size(const char *path)
+int ll_merge_marker_size(struct index_state *istate, const char *path)
{
static struct attr_check *check;
int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
if (!check)
check = attr_check_initl("conflict-marker-size", NULL);
- git_check_attr(&the_index, path, check);
+ git_check_attr(istate, path, check);
if (check->items[0].value) {
marker_size = atoi(check->items[0].value);
if (marker_size <= 0)
diff --git a/ll-merge.h b/ll-merge.h
index b72b19921e..6c6e22e40d 100644
--- a/ll-merge.h
+++ b/ll-merge.h
@@ -7,6 +7,8 @@
#include "xdiff/xdiff.h"
+struct index_state;
+
struct ll_merge_options {
unsigned virtual_ancestor : 1;
unsigned variant : 2; /* favor ours, favor theirs, or union merge */
@@ -19,8 +21,9 @@ int ll_merge(mmbuffer_t *result_buf,
mmfile_t *ancestor, const char *ancestor_label,
mmfile_t *ours, const char *our_label,
mmfile_t *theirs, const char *their_label,
+ struct index_state *istate,
const struct ll_merge_options *opts);
-int ll_merge_marker_size(const char *path);
+int ll_merge_marker_size(struct index_state *istate, const char *path);
#endif
diff --git a/merge-blobs.c b/merge-blobs.c
index fabb8c19ce..ee0a0e90c9 100644
--- a/merge-blobs.c
+++ b/merge-blobs.c
@@ -29,7 +29,12 @@ static void free_mmfile(mmfile_t *f)
free(f->ptr);
}
-static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our, mmfile_t *their, unsigned long *size)
+static void *three_way_filemerge(struct index_state *istate,
+ const char *path,
+ mmfile_t *base,
+ mmfile_t *our,
+ mmfile_t *their,
+ unsigned long *size)
{
int merge_status;
mmbuffer_t res;
@@ -41,7 +46,8 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
* common ancestor.
*/
merge_status = ll_merge(&res, path, base, NULL,
- our, ".our", their, ".their", NULL);
+ our, ".our", their, ".their",
+ istate, NULL);
if (merge_status < 0)
return NULL;
@@ -49,7 +55,9 @@ static void *three_way_filemerge(const char *path, mmfile_t *base, mmfile_t *our
return res.ptr;
}
-void *merge_blobs(const char *path, struct blob *base, struct blob *our, struct blob *their, unsigned long *size)
+void *merge_blobs(struct index_state *istate, const char *path,
+ struct blob *base, struct blob *our,
+ struct blob *their, unsigned long *size)
{
void *res = NULL;
mmfile_t f1, f2, common;
@@ -82,7 +90,7 @@ void *merge_blobs(const char *path, struct blob *base, struct blob *our, struct
common.ptr = xstrdup("");
common.size = 0;
}
- res = three_way_filemerge(path, &common, &f1, &f2, size);
+ res = three_way_filemerge(istate, path, &common, &f1, &f2, size);
free_mmfile(&common);
out_free_f2_f1:
free_mmfile(&f2);
diff --git a/merge-blobs.h b/merge-blobs.h
index 62b569e472..cc31038b80 100644
--- a/merge-blobs.h
+++ b/merge-blobs.h
@@ -1,8 +1,11 @@
#ifndef MERGE_BLOBS_H
#define MERGE_BLOBS_H
-#include "blob.h"
+struct blob;
+struct index_state;
-extern void *merge_blobs(const char *, struct blob *, struct blob *, struct blob *, unsigned long *);
+extern void *merge_blobs(struct index_state *, const char *,
+ struct blob *, struct blob *,
+ struct blob *, unsigned long *);
#endif /* MERGE_BLOBS_H */
diff --git a/merge-recursive.c b/merge-recursive.c
index fa87341e67..c0fb83d285 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1084,7 +1084,8 @@ static int merge_3way(struct merge_options *o,
read_mmblob(&src2, &b->oid);
merge_status = ll_merge(result_buf, a->path, &orig, base_name,
- &src1, name1, &src2, name2, &ll_opts);
+ &src1, name1, &src2, name2,
+ &the_index, &ll_opts);
free(base_name);
free(name1);
@@ -1115,7 +1116,7 @@ static int find_first_merges(struct object_array *result, const char *path,
/* get all revisions that merge commit a */
xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
oid_to_hex(&a->object.oid));
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
rev_opts.submodule = path;
/* FIXME: can't handle linked worktrees in submodules yet */
revs.single_worktree = path != NULL;
@@ -1274,14 +1275,14 @@ static int merge_submodule(struct merge_options *o,
return 0;
}
-static int merge_file_1(struct merge_options *o,
- const struct diff_filespec *one,
- const struct diff_filespec *a,
- const struct diff_filespec *b,
- const char *filename,
- const char *branch1,
- const char *branch2,
- struct merge_file_info *result)
+static int merge_mode_and_contents(struct merge_options *o,
+ const struct diff_filespec *one,
+ const struct diff_filespec *a,
+ const struct diff_filespec *b,
+ const char *filename,
+ const char *branch1,
+ const char *branch2,
+ struct merge_file_info *result)
{
result->merge = 0;
result->clean = 1;
@@ -1366,56 +1367,6 @@ static int merge_file_1(struct merge_options *o,
return 0;
}
-static int merge_file_special_markers(struct merge_options *o,
- const struct diff_filespec *one,
- const struct diff_filespec *a,
- const struct diff_filespec *b,
- const char *target_filename,
- const char *branch1,
- const char *filename1,
- const char *branch2,
- const char *filename2,
- struct merge_file_info *mfi)
-{
- char *side1 = NULL;
- char *side2 = NULL;
- int ret;
-
- if (filename1)
- side1 = xstrfmt("%s:%s", branch1, filename1);
- if (filename2)
- side2 = xstrfmt("%s:%s", branch2, filename2);
-
- ret = merge_file_1(o, one, a, b, target_filename,
- side1 ? side1 : branch1,
- side2 ? side2 : branch2, mfi);
-
- free(side1);
- free(side2);
- return ret;
-}
-
-static int merge_file_one(struct merge_options *o,
- const char *path,
- const struct object_id *o_oid, int o_mode,
- const struct object_id *a_oid, int a_mode,
- const struct object_id *b_oid, int b_mode,
- const char *branch1,
- const char *branch2,
- struct merge_file_info *mfi)
-{
- struct diff_filespec one, a, b;
-
- one.path = a.path = b.path = (char *)path;
- oidcpy(&one.oid, o_oid);
- one.mode = o_mode;
- oidcpy(&a.oid, a_oid);
- a.mode = a_mode;
- oidcpy(&b.oid, b_oid);
- b.mode = b_mode;
- return merge_file_1(o, &one, &a, &b, path, branch1, branch2, mfi);
-}
-
static int handle_rename_via_dir(struct merge_options *o,
struct diff_filepair *pair,
const char *rename_branch,
@@ -1659,11 +1610,8 @@ static int handle_rename_rename_1to2(struct merge_options *o,
struct merge_file_info mfi;
struct diff_filespec other;
struct diff_filespec *add;
- if (merge_file_one(o, one->path,
- &one->oid, one->mode,
- &a->oid, a->mode,
- &b->oid, b->mode,
- ci->branch1, ci->branch2, &mfi))
+ if (merge_mode_and_contents(o, one, a, b, one->path,
+ ci->branch1, ci->branch2, &mfi))
return -1;
/*
@@ -1729,14 +1677,10 @@ static int handle_rename_rename_2to1(struct merge_options *o,
path_side_1_desc = xstrfmt("%s (was %s)", path, a->path);
path_side_2_desc = xstrfmt("%s (was %s)", path, b->path);
- if (merge_file_special_markers(o, a, c1, &ci->ren1_other,
- path_side_1_desc,
- o->branch1, c1->path,
- o->branch2, ci->ren1_other.path, &mfi_c1) ||
- merge_file_special_markers(o, b, &ci->ren2_other, c2,
- path_side_2_desc,
- o->branch1, ci->ren2_other.path,
- o->branch2, c2->path, &mfi_c2))
+ if (merge_mode_and_contents(o, a, c1, &ci->ren1_other, path_side_1_desc,
+ o->branch1, o->branch2, &mfi_c1) ||
+ merge_mode_and_contents(o, b, &ci->ren2_other, c2, path_side_2_desc,
+ o->branch1, o->branch2, &mfi_c2))
return -1;
free(path_side_1_desc);
free(path_side_2_desc);
@@ -1813,7 +1757,7 @@ static struct diff_queue_struct *get_diffpairs(struct merge_options *o,
struct diff_queue_struct *ret;
struct diff_options opts;
- diff_setup(&opts);
+ repo_diff_setup(the_repository, &opts);
opts.flags.recursive = 1;
opts.flags.rename_empty = 0;
opts.detect_rename = merge_detect_rename(o);
@@ -2766,12 +2710,23 @@ static int process_renames(struct merge_options *o,
ren1_dst, branch2);
if (o->call_depth) {
struct merge_file_info mfi;
- if (merge_file_one(o, ren1_dst, &null_oid, 0,
- &ren1->pair->two->oid,
- ren1->pair->two->mode,
- &dst_other.oid,
- dst_other.mode,
- branch1, branch2, &mfi)) {
+ struct diff_filespec one, a, b;
+
+ oidcpy(&one.oid, &null_oid);
+ one.mode = 0;
+ one.path = ren1->pair->two->path;
+
+ oidcpy(&a.oid, &ren1->pair->two->oid);
+ a.mode = ren1->pair->two->mode;
+ a.path = one.path;
+
+ oidcpy(&b.oid, &dst_other.oid);
+ b.mode = dst_other.mode;
+ b.path = one.path;
+
+ if (merge_mode_and_contents(o, &one, &a, &b, ren1_dst,
+ branch1, branch2,
+ &mfi)) {
clean_merge = -1;
goto cleanup_and_return;
}
@@ -3021,13 +2976,13 @@ static int handle_modify_delete(struct merge_options *o,
_("modify"), _("modified"));
}
-static int merge_content(struct merge_options *o,
- const char *path,
- int is_dirty,
- struct object_id *o_oid, int o_mode,
- struct object_id *a_oid, int a_mode,
- struct object_id *b_oid, int b_mode,
- struct rename_conflict_info *rename_conflict_info)
+static int handle_content_merge(struct merge_options *o,
+ const char *path,
+ int is_dirty,
+ struct object_id *o_oid, int o_mode,
+ struct object_id *a_oid, int a_mode,
+ struct object_id *b_oid, int b_mode,
+ struct rename_conflict_info *rename_conflict_info)
{
const char *reason = _("content");
const char *path1 = NULL, *path2 = NULL;
@@ -3059,14 +3014,16 @@ static int merge_content(struct merge_options *o,
path2 = (rename_conflict_info->pair2 ||
o->branch2 == rename_conflict_info->branch1) ?
pair1->two->path : pair1->one->path;
+ one.path = pair1->one->path;
+ a.path = (char *)path1;
+ b.path = (char *)path2;
if (dir_in_way(path, !o->call_depth,
S_ISGITLINK(pair1->two->mode)))
df_conflict_remains = 1;
}
- if (merge_file_special_markers(o, &one, &a, &b, path,
- o->branch1, path1,
- o->branch2, path2, &mfi))
+ if (merge_mode_and_contents(o, &one, &a, &b, path,
+ o->branch1, o->branch2, &mfi))
return -1;
/*
@@ -3157,9 +3114,9 @@ static int handle_rename_normal(struct merge_options *o,
struct rename_conflict_info *ci)
{
/* Merge the content and write it out */
- return merge_content(o, path, was_dirty(o, path),
- o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
- ci);
+ return handle_content_merge(o, path, was_dirty(o, path),
+ o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
+ ci);
}
/* Per entry merge function */
@@ -3283,9 +3240,11 @@ static int process_entry(struct merge_options *o,
/* Case C: Added in both (check for same permissions) and */
/* case D: Modified in both, but differently. */
int is_dirty = 0; /* unpack_trees would have bailed if dirty */
- clean_merge = merge_content(o, path, is_dirty,
- o_oid, o_mode, a_oid, a_mode, b_oid, b_mode,
- NULL);
+ clean_merge = handle_content_merge(o, path, is_dirty,
+ o_oid, o_mode,
+ a_oid, a_mode,
+ b_oid, b_mode,
+ NULL);
} else if (!o_oid && !a_oid && !b_oid) {
/*
* this entry was deleted altogether. a_mode == 0 means
diff --git a/merge.c b/merge.c
index e30e03fb84..91008f7602 100644
--- a/merge.c
+++ b/merge.c
@@ -14,7 +14,8 @@ static const char *merge_argument(struct commit *commit)
return oid_to_hex(commit ? &commit->object.oid : the_hash_algo->empty_tree);
}
-int try_merge_command(const char *strategy, size_t xopts_nr,
+int try_merge_command(struct repository *r,
+ const char *strategy, size_t xopts_nr,
const char **xopts, struct commit_list *common,
const char *head_arg, struct commit_list *remotes)
{
@@ -35,15 +36,16 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
argv_array_clear(&args);
- discard_cache();
- if (read_cache() < 0)
+ discard_index(r->index);
+ if (read_index(r->index) < 0)
die(_("failed to read the cache"));
- resolve_undo_clear();
+ resolve_undo_clear_index(r->index);
return ret;
}
-int checkout_fast_forward(const struct object_id *head,
+int checkout_fast_forward(struct repository *r,
+ const struct object_id *head,
const struct object_id *remote,
int overwrite_ignore)
{
@@ -54,7 +56,7 @@ int checkout_fast_forward(const struct object_id *head,
struct dir_struct dir;
struct lock_file lock_file = LOCK_INIT;
- refresh_cache(REFRESH_QUIET);
+ refresh_index(r->index, REFRESH_QUIET, NULL, NULL, NULL);
if (hold_locked_index(&lock_file, LOCK_REPORT_ON_ERROR) < 0)
return -1;
@@ -86,8 +88,8 @@ int checkout_fast_forward(const struct object_id *head,
}
opts.head_idx = 1;
- opts.src_index = &the_index;
- opts.dst_index = &the_index;
+ opts.src_index = r->index;
+ opts.dst_index = r->index;
opts.update = 1;
opts.verbose_update = 1;
opts.merge = 1;
@@ -101,7 +103,7 @@ int checkout_fast_forward(const struct object_id *head,
}
clear_unpack_trees_porcelain(&opts);
- if (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))
+ if (write_locked_index(r->index, &lock_file, COMMIT_LOCK))
return error(_("unable to write new index file"));
return 0;
}
diff --git a/midx.c b/midx.c
index 713d6f9dde..ea2f3ffe2e 100644
--- a/midx.c
+++ b/midx.c
@@ -282,8 +282,8 @@ static int nth_midxed_pack_entry(struct multi_pack_index *m, struct pack_entry *
struct object_id oid;
nth_midxed_object_oid(&oid, m, pos);
for (i = 0; i < p->num_bad_objects; i++)
- if (!hashcmp(oid.hash,
- p->bad_object_sha1 + the_hash_algo->rawsz * i))
+ if (hasheq(oid.hash,
+ p->bad_object_sha1 + the_hash_algo->rawsz * i))
return 0;
}
@@ -580,8 +580,8 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,
* Take only the first duplicate.
*/
for (cur_object = 0; cur_object < nr_fanout; cur_object++) {
- if (cur_object && !oidcmp(&entries_by_fanout[cur_object - 1].oid,
- &entries_by_fanout[cur_object].oid))
+ if (cur_object && oideq(&entries_by_fanout[cur_object - 1].oid,
+ &entries_by_fanout[cur_object].oid))
continue;
ALLOC_GROW(deduplicated_entries, *nr_objects + 1, alloc_objects);
diff --git a/midx.h b/midx.h
index ce80b91c68..2d7c9c6cd1 100644
--- a/midx.h
+++ b/midx.h
@@ -3,6 +3,9 @@
#include "repository.h"
+struct object_id;
+struct pack_entry;
+
struct multi_pack_index {
struct multi_pack_index *next;
diff --git a/notes-merge.c b/notes-merge.c
index 5764e2b0ef..bd05d50b05 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -127,7 +127,7 @@ static struct notes_merge_pair *diff_tree_remote(struct notes_merge_options *o,
trace_printf("\tdiff_tree_remote(base = %.7s, remote = %.7s)\n",
oid_to_hex(base), oid_to_hex(remote));
- diff_setup(&opt);
+ repo_diff_setup(the_repository, &opt);
opt.flags.recursive = 1;
opt.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_setup_done(&opt);
@@ -190,7 +190,7 @@ static void diff_tree_local(struct notes_merge_options *o,
trace_printf("\tdiff_tree_local(len = %i, base = %.7s, local = %.7s)\n",
len, oid_to_hex(base), oid_to_hex(local));
- diff_setup(&opt);
+ repo_diff_setup(the_repository, &opt);
opt.flags.recursive = 1;
opt.output_format = DIFF_FORMAT_NO_OUTPUT;
diff_setup_done(&opt);
@@ -349,7 +349,8 @@ static int ll_merge_in_worktree(struct notes_merge_options *o,
read_mmblob(&remote, &p->remote);
status = ll_merge(&result_buf, oid_to_hex(&p->obj), &base, NULL,
- &local, o->local_ref, &remote, o->remote_ref, NULL);
+ &local, o->local_ref, &remote, o->remote_ref,
+ &the_index, NULL);
free(base.ptr);
free(local.ptr);
@@ -710,7 +711,7 @@ int notes_merge_commit(struct notes_merge_options *o,
/* write file as blob, and add to partial_tree */
if (stat(path.buf, &st))
die_errno("Failed to stat '%s'", path.buf);
- if (index_path(&blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
+ if (index_path(&the_index, &blob_oid, path.buf, &st, HASH_WRITE_OBJECT))
die("Failed to write blob object from '%s'", path.buf);
if (add_note(partial_tree, &obj_oid, &blob_oid, NULL))
die("Failed to add resolved note '%s' to notes tree",
diff --git a/oidset.c b/oidset.c
index 454c54f933..fe4eb921df 100644
--- a/oidset.c
+++ b/oidset.c
@@ -1,40 +1,37 @@
#include "cache.h"
#include "oidset.h"
+void oidset_init(struct oidset *set, size_t initial_size)
+{
+ memset(&set->set, 0, sizeof(set->set));
+ if (initial_size)
+ kh_resize_oid(&set->set, initial_size);
+}
+
int oidset_contains(const struct oidset *set, const struct object_id *oid)
{
- if (!set->map.map.tablesize)
- return 0;
- return !!oidmap_get(&set->map, oid);
+ khiter_t pos = kh_get_oid(&set->set, *oid);
+ return pos != kh_end(&set->set);
}
int oidset_insert(struct oidset *set, const struct object_id *oid)
{
- struct oidmap_entry *entry;
-
- if (!set->map.map.tablesize)
- oidmap_init(&set->map, 0);
- else if (oidset_contains(set, oid))
- return 1;
-
- entry = xmalloc(sizeof(*entry));
- oidcpy(&entry->oid, oid);
-
- oidmap_put(&set->map, entry);
- return 0;
+ int added;
+ kh_put_oid(&set->set, *oid, &added);
+ return !added;
}
int oidset_remove(struct oidset *set, const struct object_id *oid)
{
- struct oidmap_entry *entry;
-
- entry = oidmap_remove(&set->map, oid);
- free(entry);
-
- return (entry != NULL);
+ khiter_t pos = kh_get_oid(&set->set, *oid);
+ if (pos == kh_end(&set->set))
+ return 0;
+ kh_del_oid(&set->set, pos);
+ return 1;
}
void oidset_clear(struct oidset *set)
{
- oidmap_free(&set->map, 1);
+ kh_release_oid(&set->set);
+ oidset_init(set, 0);
}
diff --git a/oidset.h b/oidset.h
index 40ec5f87fe..c9d0f6d3cc 100644
--- a/oidset.h
+++ b/oidset.h
@@ -1,7 +1,8 @@
#ifndef OIDSET_H
#define OIDSET_H
-#include "oidmap.h"
+#include "hashmap.h"
+#include "khash.h"
/**
* This API is similar to sha1-array, in that it maintains a set of object ids
@@ -15,20 +16,35 @@
* table overhead.
*/
+static inline unsigned int oid_hash(struct object_id oid)
+{
+ return sha1hash(oid.hash);
+}
+
+static inline int oid_equal(struct object_id a, struct object_id b)
+{
+ return oideq(&a, &b);
+}
+
+KHASH_INIT(oid, struct object_id, int, 0, oid_hash, oid_equal)
+
/**
* A single oidset; should be zero-initialized (or use OIDSET_INIT).
*/
struct oidset {
- struct oidmap map;
+ kh_oid_t set;
};
-#define OIDSET_INIT { OIDMAP_INIT }
+#define OIDSET_INIT { { 0 } }
-static inline void oidset_init(struct oidset *set, size_t initial_size)
-{
- oidmap_init(&set->map, initial_size);
-}
+/**
+ * Initialize the oidset structure `set`.
+ *
+ * If `initial_size` is bigger than 0 then preallocate to allow inserting
+ * the specified number of elements without further allocations.
+ */
+void oidset_init(struct oidset *set, size_t initial_size);
/**
* Returns true iff `set` contains `oid`.
@@ -58,19 +74,24 @@ int oidset_remove(struct oidset *set, const struct object_id *oid);
void oidset_clear(struct oidset *set);
struct oidset_iter {
- struct oidmap_iter m_iter;
+ kh_oid_t *set;
+ khiter_t iter;
};
static inline void oidset_iter_init(struct oidset *set,
struct oidset_iter *iter)
{
- oidmap_iter_init(&set->map, &iter->m_iter);
+ iter->set = &set->set;
+ iter->iter = kh_begin(iter->set);
}
static inline struct object_id *oidset_iter_next(struct oidset_iter *iter)
{
- struct oidmap_entry *e = oidmap_iter_next(&iter->m_iter);
- return e ? &e->oid : NULL;
+ for (; iter->iter != kh_end(iter->set); iter->iter++) {
+ if (kh_exist(iter->set, iter->iter))
+ return &kh_key(iter->set, iter->iter++);
+ }
+ return NULL;
}
static inline struct object_id *oidset_iter_first(struct oidset *set,
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index fc82f37a02..f2fd9d81ef 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -262,7 +262,7 @@ void bitmap_writer_build(struct packing_data *to_pack)
if (writer.show_progress)
writer.progress = start_progress("Building bitmaps", writer.selected_nr);
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
revs.tag_objects = 1;
revs.tree_objects = 1;
revs.blob_objects = 1;
diff --git a/packfile.c b/packfile.c
index 841b36182f..86074a76e9 100644
--- a/packfile.c
+++ b/packfile.c
@@ -80,10 +80,8 @@ void pack_report(void)
static int check_packed_git_idx(const char *path, struct packed_git *p)
{
void *idx_map;
- struct pack_idx_header *hdr;
size_t idx_size;
- uint32_t version, nr, i, *index;
- int fd = git_open(path);
+ int fd = git_open(path), ret;
struct stat st;
const unsigned int hashsz = the_hash_algo->rawsz;
@@ -101,16 +99,32 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
idx_map = xmmap(NULL, idx_size, PROT_READ, MAP_PRIVATE, fd, 0);
close(fd);
- hdr = idx_map;
+ ret = load_idx(path, hashsz, idx_map, idx_size, p);
+
+ if (ret)
+ munmap(idx_map, idx_size);
+
+ return ret;
+}
+
+int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+ size_t idx_size, struct packed_git *p)
+{
+ struct pack_idx_header *hdr = idx_map;
+ uint32_t version, nr, i, *index;
+
+ if (idx_size < 4 * 256 + hashsz + hashsz)
+ return error("index file %s is too small", path);
+ if (idx_map == NULL)
+ return error("empty data");
+
if (hdr->idx_signature == htonl(PACK_IDX_SIGNATURE)) {
version = ntohl(hdr->idx_version);
- if (version < 2 || version > 2) {
- munmap(idx_map, idx_size);
+ if (version < 2 || version > 2)
return error("index file %s is version %"PRIu32
" and is not supported by this binary"
" (try upgrading GIT to a newer version)",
path, version);
- }
} else
version = 1;
@@ -120,10 +134,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
index += 2; /* skip index header */
for (i = 0; i < 256; i++) {
uint32_t n = ntohl(index[i]);
- if (n < nr) {
- munmap(idx_map, idx_size);
+ if (n < nr)
return error("non-monotonic index %s", path);
- }
nr = n;
}
@@ -135,10 +147,8 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
* - hash of the packfile
* - file checksum
*/
- if (idx_size != 4*256 + nr * (hashsz + 4) + hashsz + hashsz) {
- munmap(idx_map, idx_size);
+ if (idx_size != 4 * 256 + nr * (hashsz + 4) + hashsz + hashsz)
return error("wrong index v1 file size in %s", path);
- }
} else if (version == 2) {
/*
* Minimum size:
@@ -157,20 +167,16 @@ static int check_packed_git_idx(const char *path, struct packed_git *p)
unsigned long max_size = min_size;
if (nr)
max_size += (nr - 1)*8;
- if (idx_size < min_size || idx_size > max_size) {
- munmap(idx_map, idx_size);
+ if (idx_size < min_size || idx_size > max_size)
return error("wrong index v2 file size in %s", path);
- }
if (idx_size != min_size &&
/*
* make sure we can deal with large pack offsets.
* 31-bit signed offset won't be enough, neither
* 32-bit unsigned one will be.
*/
- (sizeof(off_t) <= 4)) {
- munmap(idx_map, idx_size);
+ (sizeof(off_t) <= 4))
return error("pack too large for current definition of off_t in %s", path);
- }
}
p->index_version = version;
diff --git a/packfile.h b/packfile.h
index 442625723d..6c4037605d 100644
--- a/packfile.h
+++ b/packfile.h
@@ -164,4 +164,17 @@ extern int has_pack_index(const unsigned char *sha1);
*/
extern int is_promisor_object(const struct object_id *oid);
+/*
+ * Expose a function for fuzz testing.
+ *
+ * load_idx() parses a block of memory as a packfile index and puts the results
+ * into a struct packed_git.
+ *
+ * This function should not be used directly. It is exposed here only so that we
+ * have a convenient entry-point for fuzz testing. For real uses, you should
+ * probably use open_pack_index() or parse_pack_index() instead.
+ */
+extern int load_idx(const char *path, const unsigned int hashsz, void *idx_map,
+ size_t idx_size, struct packed_git *p);
+
#endif
diff --git a/patch-ids.c b/patch-ids.c
index 960ea24054..c262e1be9c 100644
--- a/patch-ids.c
+++ b/patch-ids.c
@@ -56,10 +56,10 @@ static int patch_id_neq(const void *cmpfn_data,
return !oideq(&a->patch_id, &b->patch_id);
}
-int init_patch_ids(struct patch_ids *ids)
+int init_patch_ids(struct repository *r, struct patch_ids *ids)
{
memset(ids, 0, sizeof(*ids));
- diff_setup(&ids->diffopts);
+ repo_diff_setup(r, &ids->diffopts);
ids->diffopts.detect_rename = 0;
ids->diffopts.flags.recursive = 1;
diff_setup_done(&ids->diffopts);
diff --git a/patch-ids.h b/patch-ids.h
index 79ac9a8498..82a12b66f8 100644
--- a/patch-ids.h
+++ b/patch-ids.h
@@ -6,6 +6,7 @@
struct commit;
struct object_id;
+struct repository;
struct patch_id {
struct hashmap_entry ent;
@@ -20,7 +21,7 @@ struct patch_ids {
int commit_patch_id(struct commit *commit, struct diff_options *options,
struct object_id *oid, int);
-int init_patch_ids(struct patch_ids *);
+int init_patch_ids(struct repository *, struct patch_ids *);
int free_patch_ids(struct patch_ids *);
struct patch_id *add_commit_patch_id(struct commit *, struct patch_ids *);
struct patch_id *has_commit_patch_id(struct commit *, struct patch_ids *);
diff --git a/preload-index.c b/preload-index.c
index f7365761f4..9e7152ab14 100644
--- a/preload-index.c
+++ b/preload-index.c
@@ -5,10 +5,13 @@
#include "pathspec.h"
#include "dir.h"
#include "fsmonitor.h"
+#include "config.h"
+#include "progress.h"
#ifdef NO_PTHREADS
static void preload_index(struct index_state *index,
- const struct pathspec *pathspec)
+ const struct pathspec *pathspec,
+ unsigned int refresh_flags)
{
; /* nothing */
}
@@ -25,16 +28,23 @@ static void preload_index(struct index_state *index,
#define MAX_PARALLEL (20)
#define THREAD_COST (500)
+struct progress_data {
+ unsigned long n;
+ struct progress *progress;
+ pthread_mutex_t mutex;
+};
+
struct thread_data {
pthread_t pthread;
struct index_state *index;
struct pathspec pathspec;
+ struct progress_data *progress;
int offset, nr;
};
static void *preload_thread(void *_data)
{
- int nr;
+ int nr, last_nr;
struct thread_data *p = _data;
struct index_state *index = p->index;
struct cache_entry **cep = index->cache + p->offset;
@@ -43,6 +53,7 @@ static void *preload_thread(void *_data)
nr = p->nr;
if (nr + p->offset > index->cache_nr)
nr = index->cache_nr - p->offset;
+ last_nr = nr;
do {
struct cache_entry *ce = *cep++;
@@ -58,6 +69,15 @@ static void *preload_thread(void *_data)
continue;
if (ce->ce_flags & CE_FSMONITOR_VALID)
continue;
+ if (p->progress && !(nr & 31)) {
+ struct progress_data *pd = p->progress;
+
+ pthread_mutex_lock(&pd->mutex);
+ pd->n += last_nr - nr;
+ display_progress(pd->progress, pd->n);
+ pthread_mutex_unlock(&pd->mutex);
+ last_nr = nr;
+ }
if (!ce_path_match(index, ce, &p->pathspec, NULL))
continue;
if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
@@ -69,21 +89,30 @@ static void *preload_thread(void *_data)
ce_mark_uptodate(ce);
mark_fsmonitor_valid(ce);
} while (--nr > 0);
+ if (p->progress) {
+ struct progress_data *pd = p->progress;
+
+ pthread_mutex_lock(&pd->mutex);
+ display_progress(pd->progress, pd->n + last_nr);
+ pthread_mutex_unlock(&pd->mutex);
+ }
cache_def_clear(&cache);
return NULL;
}
static void preload_index(struct index_state *index,
- const struct pathspec *pathspec)
+ const struct pathspec *pathspec,
+ unsigned int refresh_flags)
{
int threads, i, work, offset;
struct thread_data data[MAX_PARALLEL];
+ struct progress_data pd;
if (!core_preload_index)
return;
threads = index->cache_nr / THREAD_COST;
- if ((index->cache_nr > 1) && (threads < 2) && getenv("GIT_FORCE_PRELOAD_TEST"))
+ if ((index->cache_nr > 1) && (threads < 2) && git_env_bool("GIT_TEST_PRELOAD_INDEX", 0))
threads = 2;
if (threads < 2)
return;
@@ -93,6 +122,13 @@ static void preload_index(struct index_state *index,
offset = 0;
work = DIV_ROUND_UP(index->cache_nr, threads);
memset(&data, 0, sizeof(data));
+
+ memset(&pd, 0, sizeof(pd));
+ if (refresh_flags & REFRESH_PROGRESS && isatty(2)) {
+ pd.progress = start_delayed_progress(_("Refreshing index"), index->cache_nr);
+ pthread_mutex_init(&pd.mutex, NULL);
+ }
+
for (i = 0; i < threads; i++) {
struct thread_data *p = data+i;
p->index = index;
@@ -100,6 +136,8 @@ static void preload_index(struct index_state *index,
copy_pathspec(&p->pathspec, pathspec);
p->offset = offset;
p->nr = work;
+ if (pd.progress)
+ p->progress = &pd;
offset += work;
if (pthread_create(&p->pthread, NULL, preload_thread, p))
die("unable to create threaded lstat");
@@ -109,15 +147,18 @@ static void preload_index(struct index_state *index,
if (pthread_join(p->pthread, NULL))
die("unable to join threaded lstat");
}
+ stop_progress(&pd.progress);
+
trace_performance_leave("preload index");
}
#endif
int read_index_preload(struct index_state *index,
- const struct pathspec *pathspec)
+ const struct pathspec *pathspec,
+ unsigned int refresh_flags)
{
int retval = read_index(index);
- preload_index(index, pathspec);
+ preload_index(index, pathspec, refresh_flags);
return retval;
}
diff --git a/read-cache.c b/read-cache.c
index 8d04d78a58..d57958233e 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -23,6 +23,8 @@
#include "split-index.h"
#include "utf8.h"
#include "fsmonitor.h"
+#include "thread-utils.h"
+#include "progress.h"
/* Mask for the name length in ce_flags in the on-disk index */
@@ -43,6 +45,8 @@
#define CACHE_EXT_LINK 0x6c696e6b /* "link" */
#define CACHE_EXT_UNTRACKED 0x554E5452 /* "UNTR" */
#define CACHE_EXT_FSMONITOR 0x46534D4E /* "FSMN" */
+#define CACHE_EXT_ENDOFINDEXENTRIES 0x454F4945 /* "EOIE" */
+#define CACHE_EXT_INDEXENTRYOFFSETTABLE 0x49454F54 /* "IEOT" */
/* changes that can be kept in $GIT_DIR/index (basically all extensions) */
#define EXTMASK (RESOLVE_UNDO_CHANGED | CACHE_TREE_CHANGED | \
@@ -205,14 +209,16 @@ void fill_stat_cache_info(struct cache_entry *ce, struct stat *st)
}
}
-static int ce_compare_data(const struct cache_entry *ce, struct stat *st)
+static int ce_compare_data(struct index_state *istate,
+ const struct cache_entry *ce,
+ struct stat *st)
{
int match = -1;
int fd = git_open_cloexec(ce->name, O_RDONLY);
if (fd >= 0) {
struct object_id oid;
- if (!index_fd(&oid, fd, st, OBJ_BLOB, ce->name, 0))
+ if (!index_fd(istate, &oid, fd, st, OBJ_BLOB, ce->name, 0))
match = !oideq(&oid, &ce->oid);
/* index_fd() closed the file descriptor already */
}
@@ -257,11 +263,13 @@ static int ce_compare_gitlink(const struct cache_entry *ce)
return !oideq(&oid, &ce->oid);
}
-static int ce_modified_check_fs(const struct cache_entry *ce, struct stat *st)
+static int ce_modified_check_fs(struct index_state *istate,
+ const struct cache_entry *ce,
+ struct stat *st)
{
switch (st->st_mode & S_IFMT) {
case S_IFREG:
- if (ce_compare_data(ce, st))
+ if (ce_compare_data(istate, ce, st))
return DATA_CHANGED;
break;
case S_IFLNK:
@@ -337,7 +345,7 @@ static int is_racy_stat(const struct index_state *istate,
);
}
-static int is_racy_timestamp(const struct index_state *istate,
+int is_racy_timestamp(const struct index_state *istate,
const struct cache_entry *ce)
{
return (!S_ISGITLINK(ce->ce_mode) &&
@@ -407,7 +415,7 @@ int ie_match_stat(struct index_state *istate,
if (assume_racy_is_modified)
changed |= DATA_CHANGED;
else
- changed |= ce_modified_check_fs(ce, st);
+ changed |= ce_modified_check_fs(istate, ce, st);
}
return changed;
@@ -447,7 +455,7 @@ int ie_modified(struct index_state *istate,
(S_ISGITLINK(ce->ce_mode) || ce->ce_stat_data.sd_size != 0))
return changed;
- changed_fs = ce_modified_check_fs(ce, st);
+ changed_fs = ce_modified_check_fs(istate, ce, st);
if (changed_fs)
return changed | changed_fs;
return 0;
@@ -753,7 +761,7 @@ int add_to_index(struct index_state *istate, const char *path, struct stat *st,
}
}
if (!intent_only) {
- if (index_path(&ce->oid, path, st, newflags)) {
+ if (index_path(istate, &ce->oid, path, st, newflags)) {
discard_cache_entry(ce);
return error("unable to index file %s", path);
}
@@ -823,7 +831,7 @@ struct cache_entry *make_cache_entry(struct index_state *istate,
ce->ce_namelen = len;
ce->ce_mode = create_ce_mode(mode);
- ret = refresh_cache_entry(&the_index, ce, refresh_options);
+ ret = refresh_cache_entry(istate, ce, refresh_options);
if (ret != ce)
discard_cache_entry(ce);
return ret;
@@ -1476,6 +1484,11 @@ int refresh_index(struct index_state *istate, unsigned int flags,
const char *typechange_fmt;
const char *added_fmt;
const char *unmerged_fmt;
+ struct progress *progress = NULL;
+
+ if (flags & REFRESH_PROGRESS && isatty(2))
+ progress = start_delayed_progress(_("Refresh index"),
+ istate->cache_nr);
trace_performance_enter();
modified_fmt = (in_porcelain ? "M\t%s\n" : "%s: needs update\n");
@@ -1493,7 +1506,7 @@ int refresh_index(struct index_state *istate, unsigned int flags,
if (ignore_submodules && S_ISGITLINK(ce->ce_mode))
continue;
- if (pathspec && !ce_path_match(&the_index, ce, pathspec, seen))
+ if (pathspec && !ce_path_match(istate, ce, pathspec, seen))
filtered = 1;
if (ce_stage(ce)) {
@@ -1516,6 +1529,8 @@ int refresh_index(struct index_state *istate, unsigned int flags,
new_entry = refresh_cache_ent(istate, ce, options, &cache_errno, &changed);
if (new_entry == ce)
continue;
+ if (progress)
+ display_progress(progress, i);
if (!new_entry) {
const char *fmt;
@@ -1547,6 +1562,10 @@ int refresh_index(struct index_state *istate, unsigned int flags,
replace_index_entry(istate, i, new_entry);
}
+ if (progress) {
+ display_progress(progress, istate->cache_nr);
+ stop_progress(&progress);
+ }
trace_performance_leave("refresh index");
return has_errors;
}
@@ -1650,7 +1669,7 @@ int verify_index_checksum;
/* Allow fsck to force verification of the cache entry order. */
int verify_ce_order;
-static int verify_hdr(struct cache_header *hdr, unsigned long size)
+static int verify_hdr(const struct cache_header *hdr, unsigned long size)
{
git_hash_ctx c;
unsigned char hash[GIT_MAX_RAWSZ];
@@ -1674,7 +1693,7 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
}
static int read_index_extension(struct index_state *istate,
- const char *ext, void *data, unsigned long sz)
+ const char *ext, const char *data, unsigned long sz)
{
switch (CACHE_EXT(ext)) {
case CACHE_EXT_TREE:
@@ -1693,6 +1712,10 @@ static int read_index_extension(struct index_state *istate,
case CACHE_EXT_FSMONITOR:
read_fsmonitor_extension(istate, data, sz);
break;
+ case CACHE_EXT_ENDOFINDEXENTRIES:
+ case CACHE_EXT_INDEXENTRYOFFSETTABLE:
+ /* already handled in do_read_index() */
+ break;
default:
if (*ext < 'A' || 'Z' < *ext)
return error("index uses %.4s extension, which we do not understand",
@@ -1713,63 +1736,25 @@ int read_index(struct index_state *istate)
return read_index_from(istate, get_index_file(), get_git_dir());
}
-static struct cache_entry *cache_entry_from_ondisk(struct mem_pool *mem_pool,
- struct ondisk_cache_entry *ondisk,
- unsigned int flags,
- const char *name,
- size_t len)
-{
- struct cache_entry *ce = mem_pool__ce_alloc(mem_pool, len);
-
- ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
- ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
- ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
- ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
- ce->ce_stat_data.sd_dev = get_be32(&ondisk->dev);
- ce->ce_stat_data.sd_ino = get_be32(&ondisk->ino);
- ce->ce_mode = get_be32(&ondisk->mode);
- ce->ce_stat_data.sd_uid = get_be32(&ondisk->uid);
- ce->ce_stat_data.sd_gid = get_be32(&ondisk->gid);
- ce->ce_stat_data.sd_size = get_be32(&ondisk->size);
- ce->ce_flags = flags & ~CE_NAMEMASK;
- ce->ce_namelen = len;
- ce->index = 0;
- hashcpy(ce->oid.hash, ondisk->sha1);
- memcpy(ce->name, name, len);
- ce->name[len] = '\0';
- return ce;
-}
-
-/*
- * Adjacent cache entries tend to share the leading paths, so it makes
- * sense to only store the differences in later entries. In the v4
- * on-disk format of the index, each on-disk cache entry stores the
- * number of bytes to be stripped from the end of the previous name,
- * and the bytes to append to the result, to come up with its name.
- */
-static unsigned long expand_name_field(struct strbuf *name, const char *cp_)
-{
- const unsigned char *ep, *cp = (const unsigned char *)cp_;
- size_t len = decode_varint(&cp);
-
- if (name->len < len)
- die("malformed name field in the index");
- strbuf_remove(name, name->len - len, len);
- for (ep = cp; *ep; ep++)
- ; /* find the end */
- strbuf_add(name, cp, ep - cp);
- return (const char *)ep + 1 - cp_;
-}
-
-static struct cache_entry *create_from_disk(struct mem_pool *mem_pool,
+static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,
+ unsigned int version,
struct ondisk_cache_entry *ondisk,
unsigned long *ent_size,
- struct strbuf *previous_name)
+ const struct cache_entry *previous_ce)
{
struct cache_entry *ce;
size_t len;
const char *name;
unsigned int flags;
+ size_t copy_len;
+ /*
+ * Adjacent cache entries tend to share the leading paths, so it makes
+ * sense to only store the differences in later entries. In the v4
+ * on-disk format of the index, each on-disk cache entry stores the
+ * number of bytes to be stripped from the end of the previous name,
+ * and the bytes to append to the result, to come up with its name.
+ */
+ int expand_name_field = version == 4;
/* On-disk flags are just 16 bits */
flags = get_be16(&ondisk->flags);
@@ -1789,21 +1774,55 @@ static struct cache_entry *create_from_disk(struct mem_pool *mem_pool,
else
name = ondisk->name;
- if (!previous_name) {
- /* v3 and earlier */
- if (len == CE_NAMEMASK)
- len = strlen(name);
- ce = cache_entry_from_ondisk(mem_pool, ondisk, flags, name, len);
+ if (expand_name_field) {
+ const unsigned char *cp = (const unsigned char *)name;
+ size_t strip_len, previous_len;
+
+ /* If we're at the begining of a block, ignore the previous name */
+ strip_len = decode_varint(&cp);
+ if (previous_ce) {
+ previous_len = previous_ce->ce_namelen;
+ if (previous_len < strip_len)
+ die(_("malformed name field in the index, near path '%s'"),
+ previous_ce->name);
+ copy_len = previous_len - strip_len;
+ } else {
+ copy_len = 0;
+ }
+ name = (const char *)cp;
+ }
- *ent_size = ondisk_ce_size(ce);
- } else {
- unsigned long consumed;
- consumed = expand_name_field(previous_name, name);
- ce = cache_entry_from_ondisk(mem_pool, ondisk, flags,
- previous_name->buf,
- previous_name->len);
+ if (len == CE_NAMEMASK) {
+ len = strlen(name);
+ if (expand_name_field)
+ len += copy_len;
+ }
+
+ ce = mem_pool__ce_alloc(ce_mem_pool, len);
+
+ ce->ce_stat_data.sd_ctime.sec = get_be32(&ondisk->ctime.sec);
+ ce->ce_stat_data.sd_mtime.sec = get_be32(&ondisk->mtime.sec);
+ ce->ce_stat_data.sd_ctime.nsec = get_be32(&ondisk->ctime.nsec);
+ ce->ce_stat_data.sd_mtime.nsec = get_be32(&ondisk->mtime.nsec);
+ ce->ce_stat_data.sd_dev = get_be32(&ondisk->dev);
+ ce->ce_stat_data.sd_ino = get_be32(&ondisk->ino);
+ ce->ce_mode = get_be32(&ondisk->mode);
+ ce->ce_stat_data.sd_uid = get_be32(&ondisk->uid);
+ ce->ce_stat_data.sd_gid = get_be32(&ondisk->gid);
+ ce->ce_stat_data.sd_size = get_be32(&ondisk->size);
+ ce->ce_flags = flags & ~CE_NAMEMASK;
+ ce->ce_namelen = len;
+ ce->index = 0;
+ hashcpy(ce->oid.hash, ondisk->sha1);
- *ent_size = (name - ((char *)ondisk)) + consumed;
+ if (expand_name_field) {
+ if (copy_len)
+ memcpy(ce->name, previous_ce->name, copy_len);
+ memcpy(ce->name + copy_len, name, len + 1 - copy_len);
+ *ent_size = (name - ((char *)ondisk)) + len + 1 - copy_len;
+ } else {
+ memcpy(ce->name, name, len + 1);
+ *ent_size = ondisk_ce_size(ce);
}
return ce;
}
@@ -1889,16 +1908,237 @@ static size_t estimate_cache_size(size_t ondisk_size, unsigned int entries)
return ondisk_size + entries * per_entry;
}
+struct index_entry_offset
+{
+ /* starting byte offset into index file, count of index entries in this block */
+ int offset, nr;
+};
+
+struct index_entry_offset_table
+{
+ int nr;
+ struct index_entry_offset entries[FLEX_ARRAY];
+};
+
+#ifndef NO_PTHREADS
+static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset);
+static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot);
+#endif
+
+static size_t read_eoie_extension(const char *mmap, size_t mmap_size);
+static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset);
+
+struct load_index_extensions
+{
+#ifndef NO_PTHREADS
+ pthread_t pthread;
+#endif
+ struct index_state *istate;
+ const char *mmap;
+ size_t mmap_size;
+ unsigned long src_offset;
+};
+
+static void *load_index_extensions(void *_data)
+{
+ struct load_index_extensions *p = _data;
+ unsigned long src_offset = p->src_offset;
+
+ while (src_offset <= p->mmap_size - the_hash_algo->rawsz - 8) {
+ /* After an array of active_nr index entries,
+ * there can be arbitrary number of extended
+ * sections, each of which is prefixed with
+ * extension name (4-byte) and section length
+ * in 4-byte network byte order.
+ */
+ uint32_t extsize = get_be32(p->mmap + src_offset + 4);
+ if (read_index_extension(p->istate,
+ p->mmap + src_offset,
+ p->mmap + src_offset + 8,
+ extsize) < 0) {
+ munmap((void *)p->mmap, p->mmap_size);
+ die(_("index file corrupt"));
+ }
+ src_offset += 8;
+ src_offset += extsize;
+ }
+
+ return NULL;
+}
+
+/*
+ * A helper function that will load the specified range of cache entries
+ * from the memory mapped file and add them to the given index.
+ */
+static unsigned long load_cache_entry_block(struct index_state *istate,
+ struct mem_pool *ce_mem_pool, int offset, int nr, const char *mmap,
+ unsigned long start_offset, const struct cache_entry *previous_ce)
+{
+ int i;
+ unsigned long src_offset = start_offset;
+
+ for (i = offset; i < offset + nr; i++) {
+ struct ondisk_cache_entry *disk_ce;
+ struct cache_entry *ce;
+ unsigned long consumed;
+
+ disk_ce = (struct ondisk_cache_entry *)(mmap + src_offset);
+ ce = create_from_disk(ce_mem_pool, istate->version, disk_ce, &consumed, previous_ce);
+ set_index_entry(istate, i, ce);
+
+ src_offset += consumed;
+ previous_ce = ce;
+ }
+ return src_offset - start_offset;
+}
+
+static unsigned long load_all_cache_entries(struct index_state *istate,
+ const char *mmap, size_t mmap_size, unsigned long src_offset)
+{
+ unsigned long consumed;
+
+ if (istate->version == 4) {
+ mem_pool_init(&istate->ce_mem_pool,
+ estimate_cache_size_from_compressed(istate->cache_nr));
+ } else {
+ mem_pool_init(&istate->ce_mem_pool,
+ estimate_cache_size(mmap_size, istate->cache_nr));
+ }
+
+ consumed = load_cache_entry_block(istate, istate->ce_mem_pool,
+ 0, istate->cache_nr, mmap, src_offset, NULL);
+ return consumed;
+}
+
+#ifndef NO_PTHREADS
+
+/*
+ * Mostly randomly chosen maximum thread counts: we
+ * cap the parallelism to online_cpus() threads, and we want
+ * to have at least 10000 cache entries per thread for it to
+ * be worth starting a thread.
+ */
+
+#define THREAD_COST (10000)
+
+struct load_cache_entries_thread_data
+{
+ pthread_t pthread;
+ struct index_state *istate;
+ struct mem_pool *ce_mem_pool;
+ int offset;
+ const char *mmap;
+ struct index_entry_offset_table *ieot;
+ int ieot_start; /* starting index into the ieot array */
+ int ieot_blocks; /* count of ieot entries to process */
+ unsigned long consumed; /* return # of bytes in index file processed */
+};
+
+/*
+ * A thread proc to run the load_cache_entries() computation
+ * across multiple background threads.
+ */
+static void *load_cache_entries_thread(void *_data)
+{
+ struct load_cache_entries_thread_data *p = _data;
+ int i;
+
+ /* iterate across all ieot blocks assigned to this thread */
+ for (i = p->ieot_start; i < p->ieot_start + p->ieot_blocks; i++) {
+ p->consumed += load_cache_entry_block(p->istate, p->ce_mem_pool,
+ p->offset, p->ieot->entries[i].nr, p->mmap, p->ieot->entries[i].offset, NULL);
+ p->offset += p->ieot->entries[i].nr;
+ }
+ return NULL;
+}
+
+static unsigned long load_cache_entries_threaded(struct index_state *istate, const char *mmap, size_t mmap_size,
+ unsigned long src_offset, int nr_threads, struct index_entry_offset_table *ieot)
+{
+ int i, offset, ieot_blocks, ieot_start, err;
+ struct load_cache_entries_thread_data *data;
+ unsigned long consumed = 0;
+
+ /* a little sanity checking */
+ if (istate->name_hash_initialized)
+ BUG("the name hash isn't thread safe");
+
+ mem_pool_init(&istate->ce_mem_pool, 0);
+
+ /* ensure we have no more threads than we have blocks to process */
+ if (nr_threads > ieot->nr)
+ nr_threads = ieot->nr;
+ data = xcalloc(nr_threads, sizeof(*data));
+
+ offset = ieot_start = 0;
+ ieot_blocks = DIV_ROUND_UP(ieot->nr, nr_threads);
+ for (i = 0; i < nr_threads; i++) {
+ struct load_cache_entries_thread_data *p = &data[i];
+ int nr, j;
+
+ if (ieot_start + ieot_blocks > ieot->nr)
+ ieot_blocks = ieot->nr - ieot_start;
+
+ p->istate = istate;
+ p->offset = offset;
+ p->mmap = mmap;
+ p->ieot = ieot;
+ p->ieot_start = ieot_start;
+ p->ieot_blocks = ieot_blocks;
+
+ /* create a mem_pool for each thread */
+ nr = 0;
+ for (j = p->ieot_start; j < p->ieot_start + p->ieot_blocks; j++)
+ nr += p->ieot->entries[j].nr;
+ if (istate->version == 4) {
+ mem_pool_init(&p->ce_mem_pool,
+ estimate_cache_size_from_compressed(nr));
+ } else {
+ mem_pool_init(&p->ce_mem_pool,
+ estimate_cache_size(mmap_size, nr));
+ }
+
+ err = pthread_create(&p->pthread, NULL, load_cache_entries_thread, p);
+ if (err)
+ die(_("unable to create load_cache_entries thread: %s"), strerror(err));
+
+ /* increment by the number of cache entries in the ieot block being processed */
+ for (j = 0; j < ieot_blocks; j++)
+ offset += ieot->entries[ieot_start + j].nr;
+ ieot_start += ieot_blocks;
+ }
+
+ for (i = 0; i < nr_threads; i++) {
+ struct load_cache_entries_thread_data *p = &data[i];
+
+ err = pthread_join(p->pthread, NULL);
+ if (err)
+ die(_("unable to join load_cache_entries thread: %s"), strerror(err));
+ mem_pool_combine(istate->ce_mem_pool, p->ce_mem_pool);
+ consumed += p->consumed;
+ }
+
+ free(data);
+
+ return consumed;
+}
+#endif
+
/* remember to discard_cache() before reading a different cache! */
int do_read_index(struct index_state *istate, const char *path, int must_exist)
{
- int fd, i;
+ int fd;
struct stat st;
unsigned long src_offset;
- struct cache_header *hdr;
- void *mmap;
+ const struct cache_header *hdr;
+ const char *mmap;
size_t mmap_size;
- struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
+ struct load_index_extensions p;
+ size_t extension_offset = 0;
+#ifndef NO_PTHREADS
+ int nr_threads, cpus;
+ struct index_entry_offset_table *ieot = NULL;
+#endif
if (istate->initialized)
return istate->cache_nr;
@@ -1924,7 +2164,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
die_errno("unable to map index file");
close(fd);
- hdr = mmap;
+ hdr = (const struct cache_header *)mmap;
if (verify_hdr(hdr, mmap_size) < 0)
goto unmap;
@@ -1935,55 +2175,74 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
istate->cache = xcalloc(istate->cache_alloc, sizeof(*istate->cache));
istate->initialized = 1;
- if (istate->version == 4) {
- previous_name = &previous_name_buf;
- mem_pool_init(&istate->ce_mem_pool,
- estimate_cache_size_from_compressed(istate->cache_nr));
- } else {
- previous_name = NULL;
- mem_pool_init(&istate->ce_mem_pool,
- estimate_cache_size(mmap_size, istate->cache_nr));
- }
+ p.istate = istate;
+ p.mmap = mmap;
+ p.mmap_size = mmap_size;
src_offset = sizeof(*hdr);
- for (i = 0; i < istate->cache_nr; i++) {
- struct ondisk_cache_entry *disk_ce;
- struct cache_entry *ce;
- unsigned long consumed;
- disk_ce = (struct ondisk_cache_entry *)((char *)mmap + src_offset);
- ce = create_from_disk(istate->ce_mem_pool, disk_ce, &consumed, previous_name);
- set_index_entry(istate, i, ce);
+#ifndef NO_PTHREADS
+ nr_threads = git_config_get_index_threads();
- src_offset += consumed;
+ /* TODO: does creating more threads than cores help? */
+ if (!nr_threads) {
+ nr_threads = istate->cache_nr / THREAD_COST;
+ cpus = online_cpus();
+ if (nr_threads > cpus)
+ nr_threads = cpus;
}
- strbuf_release(&previous_name_buf);
+
+ if (nr_threads > 1) {
+ extension_offset = read_eoie_extension(mmap, mmap_size);
+ if (extension_offset) {
+ int err;
+
+ p.src_offset = extension_offset;
+ err = pthread_create(&p.pthread, NULL, load_index_extensions, &p);
+ if (err)
+ die(_("unable to create load_index_extensions thread: %s"), strerror(err));
+
+ nr_threads--;
+ }
+ }
+
+ /*
+ * Locate and read the index entry offset table so that we can use it
+ * to multi-thread the reading of the cache entries.
+ */
+ if (extension_offset && nr_threads > 1)
+ ieot = read_ieot_extension(mmap, mmap_size, extension_offset);
+
+ if (ieot) {
+ src_offset += load_cache_entries_threaded(istate, mmap, mmap_size, src_offset, nr_threads, ieot);
+ free(ieot);
+ } else {
+ src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
+ }
+#else
+ src_offset += load_all_cache_entries(istate, mmap, mmap_size, src_offset);
+#endif
+
istate->timestamp.sec = st.st_mtime;
istate->timestamp.nsec = ST_MTIME_NSEC(st);
- while (src_offset <= mmap_size - the_hash_algo->rawsz - 8) {
- /* After an array of active_nr index entries,
- * there can be arbitrary number of extended
- * sections, each of which is prefixed with
- * extension name (4-byte) and section length
- * in 4-byte network byte order.
- */
- uint32_t extsize;
- memcpy(&extsize, (char *)mmap + src_offset + 4, 4);
- extsize = ntohl(extsize);
- if (read_index_extension(istate,
- (const char *) mmap + src_offset,
- (char *) mmap + src_offset + 8,
- extsize) < 0)
- goto unmap;
- src_offset += 8;
- src_offset += extsize;
+ /* if we created a thread, join it otherwise load the extensions on the primary thread */
+#ifndef NO_PTHREADS
+ if (extension_offset) {
+ int ret = pthread_join(p.pthread, NULL);
+ if (ret)
+ die(_("unable to join load_index_extensions thread: %s"), strerror(ret));
}
- munmap(mmap, mmap_size);
+#endif
+ if (!extension_offset) {
+ p.src_offset = src_offset;
+ load_index_extensions(&p);
+ }
+ munmap((void *)mmap, mmap_size);
return istate->cache_nr;
unmap:
- munmap(mmap, mmap_size);
+ munmap((void *)mmap, mmap_size);
die("index file corrupt");
}
@@ -2123,7 +2382,7 @@ int unmerged_index(const struct index_state *istate)
return 0;
}
-int index_has_changes(const struct index_state *istate,
+int index_has_changes(struct index_state *istate,
struct tree *tree,
struct strbuf *sb)
{
@@ -2138,7 +2397,7 @@ int index_has_changes(const struct index_state *istate,
if (tree || !get_oid_tree("HEAD", &cmp)) {
struct diff_options opt;
- diff_setup(&opt);
+ repo_diff_setup(the_repository, &opt);
opt.flags.exit_with_status = 1;
if (!sb)
opt.flags.quick = 1;
@@ -2199,11 +2458,15 @@ static int ce_write(git_hash_ctx *context, int fd, void *data, unsigned int len)
return 0;
}
-static int write_index_ext_header(git_hash_ctx *context, int fd,
- unsigned int ext, unsigned int sz)
+static int write_index_ext_header(git_hash_ctx *context, git_hash_ctx *eoie_context,
+ int fd, unsigned int ext, unsigned int sz)
{
ext = htonl(ext);
sz = htonl(sz);
+ if (eoie_context) {
+ the_hash_algo->update_fn(eoie_context, &ext, 4);
+ the_hash_algo->update_fn(eoie_context, &sz, 4);
+ }
return ((ce_write(context, fd, &ext, 4) < 0) ||
(ce_write(context, fd, &sz, 4) < 0)) ? -1 : 0;
}
@@ -2231,7 +2494,8 @@ static int ce_flush(git_hash_ctx *context, int fd, unsigned char *hash)
return (write_in_full(fd, write_buffer, left) < 0) ? -1 : 0;
}
-static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
+static void ce_smudge_racily_clean_entry(struct index_state *istate,
+ struct cache_entry *ce)
{
/*
* The only thing we care about in this function is to smudge the
@@ -2250,7 +2514,7 @@ static void ce_smudge_racily_clean_entry(struct cache_entry *ce)
return;
if (ce_match_stat_basic(ce, &st))
return;
- if (ce_modified_check_fs(ce, &st)) {
+ if (ce_modified_check_fs(istate, ce, &st)) {
/* This is "racily clean"; smudge it. Note that this
* is a tricky code. At first glance, it may appear
* that it can break with this sequence:
@@ -2446,7 +2710,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
{
uint64_t start = getnanotime();
int newfd = tempfile->fd;
- git_hash_ctx c;
+ git_hash_ctx c, eoie_c;
struct cache_header hdr;
int i, err = 0, removed, extended, hdr_version;
struct cache_entry **cache = istate->cache;
@@ -2455,6 +2719,10 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
struct ondisk_cache_entry_extended ondisk;
struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
int drop_cache_tree = istate->drop_cache_tree;
+ off_t offset;
+ int ieot_entries = 1;
+ struct index_entry_offset_table *ieot = NULL;
+ int nr, nr_threads;
for (i = removed = extended = 0; i < entries; i++) {
if (cache[i]->ce_flags & CE_REMOVE)
@@ -2488,6 +2756,46 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (ce_write(&c, newfd, &hdr, sizeof(hdr)) < 0)
return -1;
+#ifndef NO_PTHREADS
+ nr_threads = git_config_get_index_threads();
+ if (nr_threads != 1) {
+ int ieot_blocks, cpus;
+
+ /*
+ * ensure default number of ieot blocks maps evenly to the
+ * default number of threads that will process them leaving
+ * room for the thread to load the index extensions.
+ */
+ if (!nr_threads) {
+ ieot_blocks = istate->cache_nr / THREAD_COST;
+ cpus = online_cpus();
+ if (ieot_blocks > cpus - 1)
+ ieot_blocks = cpus - 1;
+ } else {
+ ieot_blocks = nr_threads;
+ if (ieot_blocks > istate->cache_nr)
+ ieot_blocks = istate->cache_nr;
+ }
+
+ /*
+ * no reason to write out the IEOT extension if we don't
+ * have enough blocks to utilize multi-threading
+ */
+ if (ieot_blocks > 1) {
+ ieot = xcalloc(1, sizeof(struct index_entry_offset_table)
+ + (ieot_blocks * sizeof(struct index_entry_offset)));
+ ieot_entries = DIV_ROUND_UP(entries, ieot_blocks);
+ }
+ }
+#endif
+
+ offset = lseek(newfd, 0, SEEK_CUR);
+ if (offset < 0) {
+ free(ieot);
+ return -1;
+ }
+ offset += write_buffer_len;
+ nr = 0;
previous_name = (hdr_version == 4) ? &previous_name_buf : NULL;
for (i = 0; i < entries; i++) {
@@ -2495,7 +2803,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
if (ce->ce_flags & CE_REMOVE)
continue;
if (!ce_uptodate(ce) && is_racy_timestamp(istate, ce))
- ce_smudge_racily_clean_entry(ce);
+ ce_smudge_racily_clean_entry(istate, ce);
if (is_null_oid(&ce->oid)) {
static const char msg[] = "cache entry has null sha1: %s";
static int allow = -1;
@@ -2509,23 +2817,79 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
drop_cache_tree = 1;
}
+ if (ieot && i && (i % ieot_entries == 0)) {
+ ieot->entries[ieot->nr].nr = nr;
+ ieot->entries[ieot->nr].offset = offset;
+ ieot->nr++;
+ /*
+ * If we have a V4 index, set the first byte to an invalid
+ * character to ensure there is nothing common with the previous
+ * entry
+ */
+ if (previous_name)
+ previous_name->buf[0] = 0;
+ nr = 0;
+ offset = lseek(newfd, 0, SEEK_CUR);
+ if (offset < 0) {
+ free(ieot);
+ return -1;
+ }
+ offset += write_buffer_len;
+ }
if (ce_write_entry(&c, newfd, ce, previous_name, (struct ondisk_cache_entry *)&ondisk) < 0)
err = -1;
if (err)
break;
+ nr++;
+ }
+ if (ieot && nr) {
+ ieot->entries[ieot->nr].nr = nr;
+ ieot->entries[ieot->nr].offset = offset;
+ ieot->nr++;
}
strbuf_release(&previous_name_buf);
- if (err)
+ if (err) {
+ free(ieot);
return err;
+ }
/* Write extension data here */
+ offset = lseek(newfd, 0, SEEK_CUR);
+ if (offset < 0) {
+ free(ieot);
+ return -1;
+ }
+ offset += write_buffer_len;
+ the_hash_algo->init_fn(&eoie_c);
+
+ /*
+ * Lets write out CACHE_EXT_INDEXENTRYOFFSETTABLE first so that we
+ * can minimize the number of extensions we have to scan through to
+ * find it during load. Write it out regardless of the
+ * strip_extensions parameter as we need it when loading the shared
+ * index.
+ */
+#ifndef NO_PTHREADS
+ if (ieot) {
+ struct strbuf sb = STRBUF_INIT;
+
+ write_ieot_extension(&sb, ieot);
+ err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_INDEXENTRYOFFSETTABLE, sb.len) < 0
+ || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+ strbuf_release(&sb);
+ free(ieot);
+ if (err)
+ return -1;
+ }
+#endif
+
if (!strip_extensions && istate->split_index) {
struct strbuf sb = STRBUF_INIT;
err = write_link_extension(&sb, istate) < 0 ||
- write_index_ext_header(&c, newfd, CACHE_EXT_LINK,
+ write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_LINK,
sb.len) < 0 ||
ce_write(&c, newfd, sb.buf, sb.len) < 0;
strbuf_release(&sb);
@@ -2536,7 +2900,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
struct strbuf sb = STRBUF_INIT;
cache_tree_write(&sb, istate->cache_tree);
- err = write_index_ext_header(&c, newfd, CACHE_EXT_TREE, sb.len) < 0
+ err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_TREE, sb.len) < 0
|| ce_write(&c, newfd, sb.buf, sb.len) < 0;
strbuf_release(&sb);
if (err)
@@ -2546,7 +2910,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
struct strbuf sb = STRBUF_INIT;
resolve_undo_write(&sb, istate->resolve_undo);
- err = write_index_ext_header(&c, newfd, CACHE_EXT_RESOLVE_UNDO,
+ err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_RESOLVE_UNDO,
sb.len) < 0
|| ce_write(&c, newfd, sb.buf, sb.len) < 0;
strbuf_release(&sb);
@@ -2557,7 +2921,7 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
struct strbuf sb = STRBUF_INIT;
write_untracked_extension(&sb, istate->untracked);
- err = write_index_ext_header(&c, newfd, CACHE_EXT_UNTRACKED,
+ err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_UNTRACKED,
sb.len) < 0 ||
ce_write(&c, newfd, sb.buf, sb.len) < 0;
strbuf_release(&sb);
@@ -2568,7 +2932,24 @@ static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
struct strbuf sb = STRBUF_INIT;
write_fsmonitor_extension(&sb, istate);
- err = write_index_ext_header(&c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
+ err = write_index_ext_header(&c, &eoie_c, newfd, CACHE_EXT_FSMONITOR, sb.len) < 0
+ || ce_write(&c, newfd, sb.buf, sb.len) < 0;
+ strbuf_release(&sb);
+ if (err)
+ return -1;
+ }
+
+ /*
+ * CACHE_EXT_ENDOFINDEXENTRIES must be written as the last entry before the SHA1
+ * so that it can be found and processed before all the index entries are
+ * read. Write it out regardless of the strip_extensions parameter as we need it
+ * when loading the shared index.
+ */
+ if (offset) {
+ struct strbuf sb = STRBUF_INIT;
+
+ write_eoie_extension(&sb, &eoie_c, offset);
+ err = write_index_ext_header(&c, NULL, newfd, CACHE_EXT_ENDOFINDEXENTRIES, sb.len) < 0
|| ce_write(&c, newfd, sb.buf, sb.len) < 0;
strbuf_release(&sb);
if (err)
@@ -2984,3 +3365,181 @@ int should_validate_cache_entries(void)
return validate_index_cache_entries;
}
+
+#define EOIE_SIZE (4 + GIT_SHA1_RAWSZ) /* <4-byte offset> + <20-byte hash> */
+#define EOIE_SIZE_WITH_HEADER (4 + 4 + EOIE_SIZE) /* <4-byte signature> + <4-byte length> + EOIE_SIZE */
+
+static size_t read_eoie_extension(const char *mmap, size_t mmap_size)
+{
+ /*
+ * The end of index entries (EOIE) extension is guaranteed to be last
+ * so that it can be found by scanning backwards from the EOF.
+ *
+ * "EOIE"
+ * <4-byte length>
+ * <4-byte offset>
+ * <20-byte hash>
+ */
+ const char *index, *eoie;
+ uint32_t extsize;
+ size_t offset, src_offset;
+ unsigned char hash[GIT_MAX_RAWSZ];
+ git_hash_ctx c;
+
+ /* ensure we have an index big enough to contain an EOIE extension */
+ if (mmap_size < sizeof(struct cache_header) + EOIE_SIZE_WITH_HEADER + the_hash_algo->rawsz)
+ return 0;
+
+ /* validate the extension signature */
+ index = eoie = mmap + mmap_size - EOIE_SIZE_WITH_HEADER - the_hash_algo->rawsz;
+ if (CACHE_EXT(index) != CACHE_EXT_ENDOFINDEXENTRIES)
+ return 0;
+ index += sizeof(uint32_t);
+
+ /* validate the extension size */
+ extsize = get_be32(index);
+ if (extsize != EOIE_SIZE)
+ return 0;
+ index += sizeof(uint32_t);
+
+ /*
+ * Validate the offset we're going to look for the first extension
+ * signature is after the index header and before the eoie extension.
+ */
+ offset = get_be32(index);
+ if (mmap + offset < mmap + sizeof(struct cache_header))
+ return 0;
+ if (mmap + offset >= eoie)
+ return 0;
+ index += sizeof(uint32_t);
+
+ /*
+ * The hash is computed over extension types and their sizes (but not
+ * their contents). E.g. if we have "TREE" extension that is N-bytes
+ * long, "REUC" extension that is M-bytes long, followed by "EOIE",
+ * then the hash would be:
+ *
+ * SHA-1("TREE" + <binary representation of N> +
+ * "REUC" + <binary representation of M>)
+ */
+ src_offset = offset;
+ the_hash_algo->init_fn(&c);
+ while (src_offset < mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER) {
+ /* After an array of active_nr index entries,
+ * there can be arbitrary number of extended
+ * sections, each of which is prefixed with
+ * extension name (4-byte) and section length
+ * in 4-byte network byte order.
+ */
+ uint32_t extsize;
+ memcpy(&extsize, mmap + src_offset + 4, 4);
+ extsize = ntohl(extsize);
+
+ /* verify the extension size isn't so large it will wrap around */
+ if (src_offset + 8 + extsize < src_offset)
+ return 0;
+
+ the_hash_algo->update_fn(&c, mmap + src_offset, 8);
+
+ src_offset += 8;
+ src_offset += extsize;
+ }
+ the_hash_algo->final_fn(hash, &c);
+ if (!hasheq(hash, (const unsigned char *)index))
+ return 0;
+
+ /* Validate that the extension offsets returned us back to the eoie extension. */
+ if (src_offset != mmap_size - the_hash_algo->rawsz - EOIE_SIZE_WITH_HEADER)
+ return 0;
+
+ return offset;
+}
+
+static void write_eoie_extension(struct strbuf *sb, git_hash_ctx *eoie_context, size_t offset)
+{
+ uint32_t buffer;
+ unsigned char hash[GIT_MAX_RAWSZ];
+
+ /* offset */
+ put_be32(&buffer, offset);
+ strbuf_add(sb, &buffer, sizeof(uint32_t));
+
+ /* hash */
+ the_hash_algo->final_fn(hash, eoie_context);
+ strbuf_add(sb, hash, the_hash_algo->rawsz);
+}
+
+#ifndef NO_PTHREADS
+#define IEOT_VERSION (1)
+
+static struct index_entry_offset_table *read_ieot_extension(const char *mmap, size_t mmap_size, size_t offset)
+{
+ const char *index = NULL;
+ uint32_t extsize, ext_version;
+ struct index_entry_offset_table *ieot;
+ int i, nr;
+
+ /* find the IEOT extension */
+ if (!offset)
+ return NULL;
+ while (offset <= mmap_size - the_hash_algo->rawsz - 8) {
+ extsize = get_be32(mmap + offset + 4);
+ if (CACHE_EXT((mmap + offset)) == CACHE_EXT_INDEXENTRYOFFSETTABLE) {
+ index = mmap + offset + 4 + 4;
+ break;
+ }
+ offset += 8;
+ offset += extsize;
+ }
+ if (!index)
+ return NULL;
+
+ /* validate the version is IEOT_VERSION */
+ ext_version = get_be32(index);
+ if (ext_version != IEOT_VERSION) {
+ error("invalid IEOT version %d", ext_version);
+ return NULL;
+ }
+ index += sizeof(uint32_t);
+
+ /* extension size - version bytes / bytes per entry */
+ nr = (extsize - sizeof(uint32_t)) / (sizeof(uint32_t) + sizeof(uint32_t));
+ if (!nr) {
+ error("invalid number of IEOT entries %d", nr);
+ return NULL;
+ }
+ ieot = xmalloc(sizeof(struct index_entry_offset_table)
+ + (nr * sizeof(struct index_entry_offset)));
+ ieot->nr = nr;
+ for (i = 0; i < nr; i++) {
+ ieot->entries[i].offset = get_be32(index);
+ index += sizeof(uint32_t);
+ ieot->entries[i].nr = get_be32(index);
+ index += sizeof(uint32_t);
+ }
+
+ return ieot;
+}
+
+static void write_ieot_extension(struct strbuf *sb, struct index_entry_offset_table *ieot)
+{
+ uint32_t buffer;
+ int i;
+
+ /* version */
+ put_be32(&buffer, IEOT_VERSION);
+ strbuf_add(sb, &buffer, sizeof(uint32_t));
+
+ /* ieot */
+ for (i = 0; i < ieot->nr; i++) {
+
+ /* offset */
+ put_be32(&buffer, ieot->entries[i].offset);
+ strbuf_add(sb, &buffer, sizeof(uint32_t));
+
+ /* count */
+ put_be32(&buffer, ieot->entries[i].nr);
+ strbuf_add(sb, &buffer, sizeof(uint32_t));
+ }
+}
+#endif
diff --git a/ref-filter.c b/ref-filter.c
index e1bcb4ca8a..2a05619211 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2010,7 +2010,7 @@ static void do_merge_filter(struct ref_filter_cbdata *ref_cbdata)
struct ref_array *array = ref_cbdata->array;
struct commit **to_clear = xcalloc(sizeof(struct commit *), array->nr);
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
for (i = 0; i < array->nr; i++) {
struct ref_array_item *item = array->items[i];
diff --git a/refs.c b/refs.c
index a7a75b4cc0..bbcac921b6 100644
--- a/refs.c
+++ b/refs.c
@@ -1394,17 +1394,50 @@ struct ref_iterator *refs_ref_iterator_begin(
* non-zero value, stop the iteration and return that value;
* otherwise, return 0.
*/
+static int do_for_each_repo_ref(struct repository *r, const char *prefix,
+ each_repo_ref_fn fn, int trim, int flags,
+ void *cb_data)
+{
+ struct ref_iterator *iter;
+ struct ref_store *refs = get_main_ref_store(r);
+
+ if (!refs)
+ return 0;
+
+ iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
+
+ return do_for_each_repo_ref_iterator(r, iter, fn, cb_data);
+}
+
+struct do_for_each_ref_help {
+ each_ref_fn *fn;
+ void *cb_data;
+};
+
+static int do_for_each_ref_helper(struct repository *r,
+ const char *refname,
+ const struct object_id *oid,
+ int flags,
+ void *cb_data)
+{
+ struct do_for_each_ref_help *hp = cb_data;
+
+ return hp->fn(refname, oid, flags, hp->cb_data);
+}
+
static int do_for_each_ref(struct ref_store *refs, const char *prefix,
each_ref_fn fn, int trim, int flags, void *cb_data)
{
struct ref_iterator *iter;
+ struct do_for_each_ref_help hp = { fn, cb_data };
if (!refs)
return 0;
iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
- return do_for_each_ref_iterator(iter, fn, cb_data);
+ return do_for_each_repo_ref_iterator(the_repository, iter,
+ do_for_each_ref_helper, &hp);
}
int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
@@ -1449,12 +1482,11 @@ int refs_for_each_fullref_in(struct ref_store *refs, const char *prefix,
return do_for_each_ref(refs, prefix, fn, 0, flag, cb_data);
}
-int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data)
+int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(get_main_ref_store(r),
- git_replace_ref_base, fn,
- strlen(git_replace_ref_base),
- DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+ return do_for_each_repo_ref(r, git_replace_ref_base, fn,
+ strlen(git_replace_ref_base),
+ DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
}
int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -2033,10 +2065,12 @@ cleanup:
int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
{
struct ref_iterator *iter;
+ struct do_for_each_ref_help hp = { fn, cb_data };
iter = refs->be->reflog_iterator_begin(refs);
- return do_for_each_ref_iterator(iter, fn, cb_data);
+ return do_for_each_repo_ref_iterator(the_repository, iter,
+ do_for_each_ref_helper, &hp);
}
int for_each_reflog(each_ref_fn fn, void *cb_data)
diff --git a/refs.h b/refs.h
index bd52c1bbae..6cc0397679 100644
--- a/refs.h
+++ b/refs.h
@@ -277,6 +277,16 @@ typedef int each_ref_fn(const char *refname,
const struct object_id *oid, int flags, void *cb_data);
/*
+ * The same as each_ref_fn, but also with a repository argument that
+ * contains the repository associated with the callback.
+ */
+typedef int each_repo_ref_fn(struct repository *r,
+ const char *refname,
+ const struct object_id *oid,
+ int flags,
+ void *cb_data);
+
+/*
* The following functions invoke the specified callback function for
* each reference indicated. If the function ever returns a nonzero
* value, stop the iteration and return that value. Please note that
@@ -309,7 +319,7 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data,
int for_each_tag_ref(each_ref_fn fn, void *cb_data);
int for_each_branch_ref(each_ref_fn fn, void *cb_data);
int for_each_remote_ref(each_ref_fn fn, void *cb_data);
-int for_each_replace_ref(struct repository *r, each_ref_fn fn, void *cb_data);
+int for_each_replace_ref(struct repository *r, each_repo_ref_fn fn, void *cb_data);
int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data);
diff --git a/refs/iterator.c b/refs/iterator.c
index 2ac91ac340..629e00a122 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -407,15 +407,15 @@ struct ref_iterator *prefix_ref_iterator_begin(struct ref_iterator *iter0,
struct ref_iterator *current_ref_iter = NULL;
-int do_for_each_ref_iterator(struct ref_iterator *iter,
- each_ref_fn fn, void *cb_data)
+int do_for_each_repo_ref_iterator(struct repository *r, struct ref_iterator *iter,
+ each_repo_ref_fn fn, void *cb_data)
{
int retval = 0, ok;
struct ref_iterator *old_ref_iter = current_ref_iter;
current_ref_iter = iter;
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- retval = fn(iter->refname, iter->oid, iter->flags, cb_data);
+ retval = fn(r, iter->refname, iter->oid, iter->flags, cb_data);
if (retval) {
/*
* If ref_iterator_abort() returns ITER_ERROR,
diff --git a/refs/packed-backend.h b/refs/packed-backend.h
index 640245d3b9..a01a0aff9c 100644
--- a/refs/packed-backend.h
+++ b/refs/packed-backend.h
@@ -1,6 +1,8 @@
#ifndef REFS_PACKED_BACKEND_H
#define REFS_PACKED_BACKEND_H
+struct ref_transaction;
+
/*
* Support for storing references in a `packed-refs` file.
*
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index eda65e73ed..3bfb89d2b3 100644
--- a/refs/ref-cache.h
+++ b/refs/ref-cache.h
@@ -1,7 +1,10 @@
#ifndef REFS_REF_CACHE_H
#define REFS_REF_CACHE_H
+#include "cache.h"
+
struct ref_dir;
+struct ref_store;
/*
* If this ref_cache is filled lazily, this function is used to load
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 04425d6d1e..f2d8c0123a 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -1,8 +1,12 @@
#ifndef REFS_REFS_INTERNAL_H
#define REFS_REFS_INTERNAL_H
+#include "cache.h"
+#include "refs.h"
#include "iterator.h"
+struct ref_transaction;
+
/*
* Data structures and functions for the internal use of the refs
* module. Code outside of the refs module should use only the public
@@ -282,7 +286,7 @@ int refs_rename_ref_available(struct ref_store *refs,
*
* // Access information about the current reference:
* if (!(iter->flags & REF_ISSYMREF))
- * printf("%s is %s\n", iter->refname, oid_to_hex(&iter->oid));
+ * printf("%s is %s\n", iter->refname, oid_to_hex(iter->oid));
*
* // If you need to peel the reference:
* ref_iterator_peel(iter, &oid);
@@ -474,8 +478,9 @@ extern struct ref_iterator *current_ref_iter;
* adapter between the callback style of reference iteration and the
* iterator style.
*/
-int do_for_each_ref_iterator(struct ref_iterator *iter,
- each_ref_fn fn, void *cb_data);
+int do_for_each_repo_ref_iterator(struct repository *r,
+ struct ref_iterator *iter,
+ each_repo_ref_fn fn, void *cb_data);
/*
* Only include per-worktree refs in a do_for_each_ref*() iteration.
diff --git a/remote.c b/remote.c
index 682f2a01f9..81f4f01b00 100644
--- a/remote.c
+++ b/remote.c
@@ -1855,7 +1855,7 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
oid_to_hex(&theirs->object.oid));
argv_array_push(&argv, "--");
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
setup_revisions(argv.argc, argv.argv, &revs, NULL);
if (prepare_revision_walk(&revs))
die("revision walk setup failed");
diff --git a/replace-object.c b/replace-object.c
index 4ec77ce418..e295e87943 100644
--- a/replace-object.c
+++ b/replace-object.c
@@ -6,7 +6,8 @@
#include "repository.h"
#include "commit.h"
-static int register_replace_ref(const char *refname,
+static int register_replace_ref(struct repository *r,
+ const char *refname,
const struct object_id *oid,
int flag, void *cb_data)
{
@@ -25,13 +26,13 @@ static int register_replace_ref(const char *refname,
oidcpy(&repl_obj->replacement, oid);
/* Register new object */
- if (oidmap_put(the_repository->objects->replace_map, repl_obj))
+ if (oidmap_put(r->objects->replace_map, repl_obj))
die(_("duplicate replace ref: %s"), refname);
return 0;
}
-static void prepare_replace_object(struct repository *r)
+void prepare_replace_object(struct repository *r)
{
if (r->objects->replace_map)
return;
diff --git a/replace-object.h b/replace-object.h
index 9345e105dd..16528df942 100644
--- a/replace-object.h
+++ b/replace-object.h
@@ -10,6 +10,8 @@ struct replace_object {
struct object_id replacement;
};
+void prepare_replace_object(struct repository *r);
+
/*
* This internal function is only declared here for the benefit of
* lookup_replace_object(). Please do not call it directly.
diff --git a/rerere.c b/rerere.c
index 7aa149e849..887e26d45b 100644
--- a/rerere.c
+++ b/rerere.c
@@ -462,11 +462,12 @@ static int handle_path(unsigned char *sha1, struct rerere_io *io, int marker_siz
* Scan the path for conflicts, do the "handle_path()" thing above, and
* return the number of conflict hunks found.
*/
-static int handle_file(const char *path, unsigned char *sha1, const char *output)
+static int handle_file(struct index_state *istate, const char *path,
+ unsigned char *sha1, const char *output)
{
int has_conflicts = 0;
struct rerere_io_file io;
- int marker_size = ll_merge_marker_size(path);
+ int marker_size = ll_merge_marker_size(istate, path);
memset(&io, 0, sizeof(io));
io.io.getline = rerere_file_getline;
@@ -511,9 +512,9 @@ static int handle_file(const char *path, unsigned char *sha1, const char *output
* stages we have already looked at in this invocation of this
* function.
*/
-static int check_one_conflict(int i, int *type)
+static int check_one_conflict(struct index_state *istate, int i, int *type)
{
- const struct cache_entry *e = active_cache[i];
+ const struct cache_entry *e = istate->cache[i];
if (!ce_stage(e)) {
*type = RESOLVED;
@@ -521,13 +522,13 @@ static int check_one_conflict(int i, int *type)
}
*type = PUNTED;
- while (i < active_nr && ce_stage(active_cache[i]) == 1)
+ while (i < istate->cache_nr && ce_stage(istate->cache[i]) == 1)
i++;
/* Only handle regular files with both stages #2 and #3 */
- if (i + 1 < active_nr) {
- const struct cache_entry *e2 = active_cache[i];
- const struct cache_entry *e3 = active_cache[i + 1];
+ if (i + 1 < istate->cache_nr) {
+ const struct cache_entry *e2 = istate->cache[i];
+ const struct cache_entry *e3 = istate->cache[i + 1];
if (ce_stage(e2) == 2 &&
ce_stage(e3) == 3 &&
ce_same_name(e, e3) &&
@@ -537,7 +538,7 @@ static int check_one_conflict(int i, int *type)
}
/* Skip the entries with the same name */
- while (i < active_nr && ce_same_name(e, active_cache[i]))
+ while (i < istate->cache_nr && ce_same_name(e, istate->cache[i]))
i++;
return i;
}
@@ -553,16 +554,17 @@ static int check_one_conflict(int i, int *type)
* are identical to the previous round, might want to be handled,
* though.
*/
-static int find_conflict(struct string_list *conflict)
+static int find_conflict(struct repository *r, struct string_list *conflict)
{
int i;
- if (read_cache() < 0)
+
+ if (read_index(r->index) < 0)
return error(_("index file corrupt"));
- for (i = 0; i < active_nr;) {
+ for (i = 0; i < r->index->cache_nr;) {
int conflict_type;
- const struct cache_entry *e = active_cache[i];
- i = check_one_conflict(i, &conflict_type);
+ const struct cache_entry *e = r->index->cache[i];
+ i = check_one_conflict(r->index, i, &conflict_type);
if (conflict_type == THREE_STAGED)
string_list_insert(conflict, (const char *)e->name);
}
@@ -584,18 +586,19 @@ static int find_conflict(struct string_list *conflict)
* NEEDSWORK: we may want to fix the caller that implements "rerere
* remaining" to do this without abusing merge_rr.
*/
-int rerere_remaining(struct string_list *merge_rr)
+int rerere_remaining(struct repository *r, struct string_list *merge_rr)
{
int i;
+
if (setup_rerere(merge_rr, RERERE_READONLY))
return 0;
- if (read_cache() < 0)
+ if (read_index(r->index) < 0)
return error(_("index file corrupt"));
- for (i = 0; i < active_nr;) {
+ for (i = 0; i < r->index->cache_nr;) {
int conflict_type;
- const struct cache_entry *e = active_cache[i];
- i = check_one_conflict(i, &conflict_type);
+ const struct cache_entry *e = r->index->cache[i];
+ i = check_one_conflict(r->index, i, &conflict_type);
if (conflict_type == PUNTED)
string_list_insert(merge_rr, (const char *)e->name);
else if (conflict_type == RESOLVED) {
@@ -615,7 +618,8 @@ int rerere_remaining(struct string_list *merge_rr)
* if that recorded conflict resolves cleanly what we
* got in the "cur".
*/
-static int try_merge(const struct rerere_id *id, const char *path,
+static int try_merge(struct index_state *istate,
+ const struct rerere_id *id, const char *path,
mmfile_t *cur, mmbuffer_t *result)
{
int ret;
@@ -629,7 +633,8 @@ static int try_merge(const struct rerere_id *id, const char *path,
* A three-way merge. Note that this honors user-customizable
* low-level merge driver settings.
*/
- ret = ll_merge(result, path, &base, NULL, cur, "", &other, "", NULL);
+ ret = ll_merge(result, path, &base, NULL, cur, "", &other, "",
+ istate, NULL);
free(base.ptr);
free(other.ptr);
@@ -647,7 +652,7 @@ static int try_merge(const struct rerere_id *id, const char *path,
* Returns 0 for successful replay of recorded resolution, or non-zero
* for failure.
*/
-static int merge(const struct rerere_id *id, const char *path)
+static int merge(struct index_state *istate, const struct rerere_id *id, const char *path)
{
FILE *f;
int ret;
@@ -658,13 +663,13 @@ static int merge(const struct rerere_id *id, const char *path)
* Normalize the conflicts in path and write it out to
* "thisimage" temporary file.
*/
- if ((handle_file(path, NULL, rerere_path(id, "thisimage")) < 0) ||
+ if ((handle_file(istate, path, NULL, rerere_path(id, "thisimage")) < 0) ||
read_mmfile(&cur, rerere_path(id, "thisimage"))) {
ret = 1;
goto out;
}
- ret = try_merge(id, path, &cur, &result);
+ ret = try_merge(istate, id, path, &cur, &result);
if (ret)
goto out;
@@ -692,7 +697,7 @@ out:
return ret;
}
-static void update_paths(struct string_list *update)
+static void update_paths(struct repository *r, struct string_list *update)
{
struct lock_file index_lock = LOCK_INIT;
int i;
@@ -701,13 +706,13 @@ static void update_paths(struct string_list *update)
for (i = 0; i < update->nr; i++) {
struct string_list_item *item = &update->items[i];
- if (add_file_to_cache(item->string, 0))
+ if (add_file_to_index(r->index, item->string, 0))
exit(128);
fprintf_ln(stderr, _("Staged '%s' using previous resolution."),
item->string);
}
- if (write_locked_index(&the_index, &index_lock,
+ if (write_locked_index(r->index, &index_lock,
COMMIT_LOCK | SKIP_IF_UNCHANGED))
die(_("unable to write new index file"));
}
@@ -726,7 +731,8 @@ static void remove_variant(struct rerere_id *id)
* only have the preimage for that conflict, in which case the result
* needs to be recorded as a resolution in a postimage file.
*/
-static void do_rerere_one_path(struct string_list_item *rr_item,
+static void do_rerere_one_path(struct index_state *istate,
+ struct string_list_item *rr_item,
struct string_list *update)
{
const char *path = rr_item->string;
@@ -738,7 +744,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
/* Has the user resolved it already? */
if (variant >= 0) {
- if (!handle_file(path, NULL, NULL)) {
+ if (!handle_file(istate, path, NULL, NULL)) {
copy_file(rerere_path(id, "postimage"), path, 0666);
id->collection->status[variant] |= RR_HAS_POSTIMAGE;
fprintf_ln(stderr, _("Recorded resolution for '%s'."), path);
@@ -762,7 +768,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
continue;
vid.variant = variant;
- if (merge(&vid, path))
+ if (merge(istate, &vid, path))
continue; /* failed to replay */
/*
@@ -787,7 +793,7 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
assign_variant(id);
variant = id->variant;
- handle_file(path, NULL, rerere_path(id, "preimage"));
+ handle_file(istate, path, NULL, rerere_path(id, "preimage"));
if (id->collection->status[variant] & RR_HAS_POSTIMAGE) {
const char *path = rerere_path(id, "postimage");
if (unlink(path))
@@ -798,13 +804,14 @@ static void do_rerere_one_path(struct string_list_item *rr_item,
fprintf_ln(stderr, _("Recorded preimage for '%s'"), path);
}
-static int do_plain_rerere(struct string_list *rr, int fd)
+static int do_plain_rerere(struct repository *r,
+ struct string_list *rr, int fd)
{
struct string_list conflict = STRING_LIST_INIT_DUP;
struct string_list update = STRING_LIST_INIT_DUP;
int i;
- find_conflict(&conflict);
+ find_conflict(r, &conflict);
/*
* MERGE_RR records paths with conflicts immediately after
@@ -823,7 +830,7 @@ static int do_plain_rerere(struct string_list *rr, int fd)
* conflict ID. No need to write anything out
* yet.
*/
- ret = handle_file(path, sha1, NULL);
+ ret = handle_file(r->index, path, sha1, NULL);
if (ret != 0 && string_list_has_string(rr, path)) {
remove_variant(string_list_lookup(rr, path)->util);
string_list_remove(rr, path, 1);
@@ -839,10 +846,10 @@ static int do_plain_rerere(struct string_list *rr, int fd)
}
for (i = 0; i < rr->nr; i++)
- do_rerere_one_path(&rr->items[i], &update);
+ do_rerere_one_path(r->index, &rr->items[i], &update);
if (update.nr)
- update_paths(&update);
+ update_paths(r, &update);
return write_rr(rr, fd);
}
@@ -897,7 +904,7 @@ int setup_rerere(struct string_list *merge_rr, int flags)
* perform mergy operations, possibly leaving conflicted index entries
* and working tree files.
*/
-int rerere(int flags)
+int repo_rerere(struct repository *r, int flags)
{
struct string_list merge_rr = STRING_LIST_INIT_DUP;
int fd, status;
@@ -905,7 +912,7 @@ int rerere(int flags)
fd = setup_rerere(&merge_rr, flags);
if (fd < 0)
return 0;
- status = do_plain_rerere(&merge_rr, fd);
+ status = do_plain_rerere(r, &merge_rr, fd);
free_rerere_dirs();
return status;
}
@@ -942,29 +949,30 @@ static int rerere_mem_getline(struct strbuf *sb, struct rerere_io *io_)
return 0;
}
-static int handle_cache(const char *path, unsigned char *sha1, const char *output)
+static int handle_cache(struct index_state *istate, const char *path,
+ unsigned char *sha1, const char *output)
{
mmfile_t mmfile[3] = {{NULL}};
mmbuffer_t result = {NULL, 0};
const struct cache_entry *ce;
int pos, len, i, has_conflicts;
struct rerere_io_mem io;
- int marker_size = ll_merge_marker_size(path);
+ int marker_size = ll_merge_marker_size(istate, path);
/*
* Reproduce the conflicted merge in-core
*/
len = strlen(path);
- pos = cache_name_pos(path, len);
+ pos = index_name_pos(istate, path, len);
if (0 <= pos)
return -1;
pos = -pos - 1;
- while (pos < active_nr) {
+ while (pos < istate->cache_nr) {
enum object_type type;
unsigned long size;
- ce = active_cache[pos++];
+ ce = istate->cache[pos++];
if (ce_namelen(ce) != len || memcmp(ce->name, path, len))
break;
i = ce_stage(ce) - 1;
@@ -984,7 +992,8 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
*/
ll_merge(&result, path, &mmfile[0], NULL,
&mmfile[1], "ours",
- &mmfile[2], "theirs", NULL);
+ &mmfile[2], "theirs",
+ istate, NULL);
for (i = 0; i < 3; i++)
free(mmfile[i].ptr);
@@ -1008,7 +1017,9 @@ static int handle_cache(const char *path, unsigned char *sha1, const char *outpu
return has_conflicts;
}
-static int rerere_forget_one_path(const char *path, struct string_list *rr)
+static int rerere_forget_one_path(struct index_state *istate,
+ const char *path,
+ struct string_list *rr)
{
const char *filename;
struct rerere_id *id;
@@ -1020,7 +1031,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
* Recreate the original conflict from the stages in the
* index and compute the conflict ID
*/
- ret = handle_cache(path, sha1, NULL);
+ ret = handle_cache(istate, path, sha1, NULL);
if (ret < 1)
return error(_("could not parse conflict hunks in '%s'"), path);
@@ -1037,13 +1048,13 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
if (!has_rerere_resolution(id))
continue;
- handle_cache(path, sha1, rerere_path(id, "thisimage"));
+ handle_cache(istate, path, sha1, rerere_path(id, "thisimage"));
if (read_mmfile(&cur, rerere_path(id, "thisimage"))) {
free(cur.ptr);
error(_("failed to update conflicted state in '%s'"), path);
goto fail_exit;
}
- cleanly_resolved = !try_merge(id, path, &cur, &result);
+ cleanly_resolved = !try_merge(istate, id, path, &cur, &result);
free(result.ptr);
free(cur.ptr);
if (cleanly_resolved)
@@ -1069,7 +1080,7 @@ static int rerere_forget_one_path(const char *path, struct string_list *rr)
* conflict in the working tree, run us again to record
* the postimage.
*/
- handle_cache(path, sha1, rerere_path(id, "preimage"));
+ handle_cache(istate, path, sha1, rerere_path(id, "preimage"));
fprintf_ln(stderr, _("Updated preimage for '%s'"), path);
/*
@@ -1087,13 +1098,13 @@ fail_exit:
return -1;
}
-int rerere_forget(struct pathspec *pathspec)
+int rerere_forget(struct repository *r, struct pathspec *pathspec)
{
int i, fd;
struct string_list conflict = STRING_LIST_INIT_DUP;
struct string_list merge_rr = STRING_LIST_INIT_DUP;
- if (read_cache() < 0)
+ if (read_index(r->index) < 0)
return error(_("index file corrupt"));
fd = setup_rerere(&merge_rr, RERERE_NOAUTOUPDATE);
@@ -1105,14 +1116,14 @@ int rerere_forget(struct pathspec *pathspec)
* recover the original conflicted state and then
* find the conflicted paths.
*/
- unmerge_cache(pathspec);
- find_conflict(&conflict);
+ unmerge_index(r->index, pathspec);
+ find_conflict(r, &conflict);
for (i = 0; i < conflict.nr; i++) {
struct string_list_item *it = &conflict.items[i];
- if (!match_pathspec(&the_index, pathspec, it->string,
+ if (!match_pathspec(r->index, pathspec, it->string,
strlen(it->string), 0, NULL, 0))
continue;
- rerere_forget_one_path(it->string, &merge_rr);
+ rerere_forget_one_path(r->index, it->string, &merge_rr);
}
return write_rr(&merge_rr, fd);
}
diff --git a/rerere.h b/rerere.h
index cd948f28f4..5ad8864b3c 100644
--- a/rerere.h
+++ b/rerere.h
@@ -4,6 +4,7 @@
#include "string-list.h"
struct pathspec;
+struct repository;
#define RERERE_AUTOUPDATE 01
#define RERERE_NOAUTOUPDATE 02
@@ -23,7 +24,10 @@ struct rerere_id {
};
int setup_rerere(struct string_list *, int);
-int rerere(int);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define rerere(flags) repo_rerere(the_repository, flags)
+#endif
+int repo_rerere(struct repository *, int);
/*
* Given the conflict ID and the name of a "file" used for replaying
* the recorded resolution (e.g. "preimage", "postimage"), return the
@@ -31,8 +35,8 @@ int rerere(int);
* return the path to the directory that houses these files.
*/
const char *rerere_path(const struct rerere_id *, const char *file);
-int rerere_forget(struct pathspec *);
-int rerere_remaining(struct string_list *);
+int rerere_forget(struct repository *, struct pathspec *);
+int rerere_remaining(struct repository *, struct string_list *);
void rerere_clear(struct string_list *);
void rerere_gc(struct string_list *);
diff --git a/revision.c b/revision.c
index e18bd530e4..b5108b75ab 100644
--- a/revision.c
+++ b/revision.c
@@ -52,7 +52,8 @@ static void mark_blob_uninteresting(struct blob *blob)
blob->object.flags |= UNINTERESTING;
}
-static void mark_tree_contents_uninteresting(struct tree *tree)
+static void mark_tree_contents_uninteresting(struct repository *r,
+ struct tree *tree)
{
struct tree_desc desc;
struct name_entry entry;
@@ -64,10 +65,10 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
while (tree_entry(&desc, &entry)) {
switch (object_type(entry.mode)) {
case OBJ_TREE:
- mark_tree_uninteresting(lookup_tree(the_repository, entry.oid));
+ mark_tree_uninteresting(r, lookup_tree(r, entry.oid));
break;
case OBJ_BLOB:
- mark_blob_uninteresting(lookup_blob(the_repository, entry.oid));
+ mark_blob_uninteresting(lookup_blob(r, entry.oid));
break;
default:
/* Subproject commit - not in this repository */
@@ -82,7 +83,7 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
free_tree_buffer(tree);
}
-void mark_tree_uninteresting(struct tree *tree)
+void mark_tree_uninteresting(struct repository *r, struct tree *tree)
{
struct object *obj;
@@ -93,7 +94,7 @@ void mark_tree_uninteresting(struct tree *tree)
if (obj->flags & UNINTERESTING)
return;
obj->flags |= UNINTERESTING;
- mark_tree_contents_uninteresting(tree);
+ mark_tree_contents_uninteresting(r, tree);
}
struct commit_stack {
@@ -199,7 +200,7 @@ void add_head_to_pending(struct rev_info *revs)
struct object *obj;
if (get_oid("HEAD", &oid))
return;
- obj = parse_object(the_repository, &oid);
+ obj = parse_object(revs->repo, &oid);
if (!obj)
return;
add_pending_object(revs, obj, "HEAD");
@@ -211,7 +212,7 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
{
struct object *object;
- object = parse_object(the_repository, oid);
+ object = parse_object(revs->repo, oid);
if (!object) {
if (revs->ignore_missing)
return object;
@@ -248,7 +249,7 @@ static struct commit *handle_commit(struct rev_info *revs,
add_pending_object(revs, object, tag->tag);
if (!tag->tagged)
die("bad tag");
- object = parse_object(the_repository, &tag->tagged->oid);
+ object = parse_object(revs->repo, &tag->tagged->oid);
if (!object) {
if (revs->ignore_missing_links || (flags & UNINTERESTING))
return NULL;
@@ -298,7 +299,7 @@ static struct commit *handle_commit(struct rev_info *revs,
if (!revs->tree_objects)
return NULL;
if (flags & UNINTERESTING) {
- mark_tree_contents_uninteresting(tree);
+ mark_tree_contents_uninteresting(revs->repo, tree);
return NULL;
}
add_pending_object_with_path(revs, object, name, mode, path);
@@ -878,7 +879,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
return;
left_first = left_count < right_count;
- init_patch_ids(&ids);
+ init_patch_ids(revs->repo, &ids);
ids.diffopts.pathspec = revs->diffopt.pathspec;
/* Compute patch-ids for one side */
@@ -1254,7 +1255,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
{
struct all_refs_cb *cb = cb_data;
if (!is_null_oid(oid)) {
- struct object *o = parse_object(the_repository, oid);
+ struct object *o = parse_object(cb->all_revs->repo, oid);
if (o) {
o->flags |= cb->all_flags;
/* ??? CMDLINEFLAGS ??? */
@@ -1313,7 +1314,7 @@ void add_reflogs_to_pending(struct rev_info *revs, unsigned flags)
cb.all_revs = revs;
cb.all_flags = flags;
- cb.refs = get_main_ref_store(the_repository);
+ cb.refs = get_main_ref_store(revs->repo);
for_each_reflog(handle_one_reflog, &cb);
if (!revs->single_worktree)
@@ -1327,7 +1328,7 @@ static void add_cache_tree(struct cache_tree *it, struct rev_info *revs,
int i;
if (it->entry_count >= 0) {
- struct tree *tree = lookup_tree(the_repository, &it->oid);
+ struct tree *tree = lookup_tree(revs->repo, &it->oid);
add_pending_object_with_path(revs, &tree->object, "",
040000, path->buf);
}
@@ -1353,7 +1354,7 @@ static void do_add_index_objects_to_pending(struct rev_info *revs,
if (S_ISGITLINK(ce->ce_mode))
continue;
- blob = lookup_blob(the_repository, &ce->oid);
+ blob = lookup_blob(revs->repo, &ce->oid);
if (!blob)
die("unable to add index blob to traversal");
add_pending_object_with_path(revs, &blob->object, "",
@@ -1371,8 +1372,8 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned int flags)
{
struct worktree **worktrees, **p;
- read_cache();
- do_add_index_objects_to_pending(revs, &the_index);
+ read_index(revs->repo->index);
+ do_add_index_objects_to_pending(revs, revs->repo->index);
if (revs->single_worktree)
return;
@@ -1440,10 +1441,13 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
return 1;
}
-void init_revisions(struct rev_info *revs, const char *prefix)
+void repo_init_revisions(struct repository *r,
+ struct rev_info *revs,
+ const char *prefix)
{
memset(revs, 0, sizeof(*revs));
+ revs->repo = r;
revs->abbrev = DEFAULT_ABBREV;
revs->ignore_merges = 1;
revs->simplify_history = 1;
@@ -1465,11 +1469,11 @@ void init_revisions(struct rev_info *revs, const char *prefix)
revs->commit_format = CMIT_FMT_DEFAULT;
revs->expand_tabs_in_log_default = 8;
- init_grep_defaults();
- grep_init(&revs->grep_filter, prefix);
+ init_grep_defaults(revs->repo);
+ grep_init(&revs->grep_filter, revs->repo, prefix);
revs->grep_filter.status_only = 1;
- diff_setup(&revs->diffopt);
+ repo_diff_setup(revs->repo, &revs->diffopt);
if (prefix && !revs->diffopt.prefix) {
revs->diffopt.prefix = prefix;
revs->diffopt.prefix_length = strlen(prefix);
@@ -1497,6 +1501,7 @@ static void prepare_show_merge(struct rev_info *revs)
struct object_id oid;
const char **prune = NULL;
int i, prune_num = 1; /* counting terminating NULL */
+ struct index_state *istate = revs->repo->index;
if (get_oid("HEAD", &oid))
die("--merge without HEAD?");
@@ -1512,20 +1517,20 @@ static void prepare_show_merge(struct rev_info *revs)
free_commit_list(bases);
head->object.flags |= SYMMETRIC_LEFT;
- if (!active_nr)
- read_cache();
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ if (!istate->cache_nr)
+ read_index(istate);
+ for (i = 0; i < istate->cache_nr; i++) {
+ const struct cache_entry *ce = istate->cache[i];
if (!ce_stage(ce))
continue;
- if (ce_path_match(&the_index, ce, &revs->prune_data, NULL)) {
+ if (ce_path_match(istate, ce, &revs->prune_data, NULL)) {
prune_num++;
REALLOC_ARRAY(prune, prune_num);
prune[prune_num-2] = ce->name;
prune[prune_num-1] = NULL;
}
- while ((i+1 < active_nr) &&
- ce_same_name(ce, active_cache[i+1]))
+ while ((i+1 < istate->cache_nr) &&
+ ce_same_name(ce, istate->cache[i+1]))
i++;
}
clear_pathspec(&revs->prune_data);
@@ -1582,8 +1587,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
*dotdot = '\0';
}
- a_obj = parse_object(the_repository, &a_oid);
- b_obj = parse_object(the_repository, &b_oid);
+ a_obj = parse_object(revs->repo, &a_oid);
+ b_obj = parse_object(revs->repo, &b_oid);
if (!a_obj || !b_obj)
return dotdot_missing(arg, dotdot, revs, symmetric);
@@ -1596,8 +1601,8 @@ static int handle_dotdot_1(const char *arg, char *dotdot,
struct commit *a, *b;
struct commit_list *exclude;
- a = lookup_commit_reference(the_repository, &a_obj->oid);
- b = lookup_commit_reference(the_repository, &b_obj->oid);
+ a = lookup_commit_reference(revs->repo, &a_obj->oid);
+ b = lookup_commit_reference(revs->repo, &b_obj->oid);
if (!a || !b)
return dotdot_missing(arg, dotdot, revs, symmetric);
@@ -2205,7 +2210,7 @@ static int handle_revision_pseudo_opt(const char *submodule,
BUG("--single-worktree cannot be used together with submodule");
refs = get_submodule_ref_store(submodule);
} else
- refs = get_main_ref_store(the_repository);
+ refs = get_main_ref_store(revs->repo);
/*
* NOTE!
@@ -2885,9 +2890,10 @@ void reset_revision_walk(void)
static int mark_uninteresting(const struct object_id *oid,
struct packed_git *pack,
uint32_t pos,
- void *unused)
+ void *cb)
{
- struct object *o = parse_object(the_repository, oid);
+ struct rev_info *revs = cb;
+ struct object *o = parse_object(revs->repo, oid);
o->flags |= UNINTERESTING | SEEN;
return 0;
}
@@ -2920,7 +2926,7 @@ int prepare_revision_walk(struct rev_info *revs)
revs->treesame.name = "treesame";
if (revs->exclude_promisor_objects) {
- for_each_packed_object(mark_uninteresting, NULL,
+ for_each_packed_object(mark_uninteresting, revs,
FOR_EACH_OBJECT_PROMISOR_ONLY);
}
diff --git a/revision.h b/revision.h
index 2b30ac270d..bc30a3023e 100644
--- a/revision.h
+++ b/revision.h
@@ -28,8 +28,9 @@
#define DECORATE_SHORT_REFS 1
#define DECORATE_FULL_REFS 2
-struct rev_info;
struct log_info;
+struct repository;
+struct rev_info;
struct string_list;
struct saved_parents;
define_shared_commit_slab(revision_sources, char *);
@@ -60,6 +61,7 @@ struct rev_info {
/* Starting list */
struct commit_list *commits;
struct object_array pending;
+ struct repository *repo;
/* Parents of shown commits */
struct object_array boundary_commits;
@@ -264,12 +266,17 @@ extern volatile show_early_output_fn_t show_early_output;
struct setup_revision_opt {
const char *def;
void (*tweak)(struct rev_info *, struct setup_revision_opt *);
- const char *submodule;
+ const char *submodule; /* TODO: drop this and use rev_info->repo */
int assume_dashdash;
unsigned revarg_opt;
};
-void init_revisions(struct rev_info *revs, const char *prefix);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
+#endif
+void repo_init_revisions(struct repository *r,
+ struct rev_info *revs,
+ const char *prefix);
int setup_revisions(int argc, const char **argv, struct rev_info *revs,
struct setup_revision_opt *);
void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
@@ -289,7 +296,7 @@ void put_revision_mark(const struct rev_info *revs,
const struct commit *commit);
void mark_parents_uninteresting(struct commit *commit);
-void mark_tree_uninteresting(struct tree *tree);
+void mark_tree_uninteresting(struct repository *r, struct tree *tree);
void show_object_with_name(FILE *, struct object *, const char *);
diff --git a/sequencer.c b/sequencer.c
index ddb41a62d9..0c164d5f98 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -474,8 +474,8 @@ static int fast_forward_to(const struct object_id *to, const struct object_id *f
struct strbuf sb = STRBUF_INIT;
struct strbuf err = STRBUF_INIT;
- read_cache();
- if (checkout_fast_forward(from, to, 1))
+ read_index(&the_index);
+ if (checkout_fast_forward(the_repository, from, to, 1))
return -1; /* the callee should have complained already */
strbuf_addf(&sb, _("%s: fast-forward"), _(action_name(opts)));
@@ -1176,7 +1176,7 @@ void print_commit_summary(const char *prefix, const struct object_id *oid,
strbuf_release(&author_ident);
strbuf_release(&committer_ident);
- init_revisions(&rev, prefix);
+ repo_init_revisions(the_repository, &rev, prefix);
setup_revisions(0, NULL, &rev, NULL);
rev.diff = 1;
@@ -1831,7 +1831,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
commit_list_insert(base, &common);
commit_list_insert(next, &remotes);
- res |= try_merge_command(opts->strategy,
+ res |= try_merge_command(the_repository, opts->strategy,
opts->xopts_nr, (const char **)opts->xopts,
common, oid_to_hex(&head), remotes);
free_commit_list(common);
@@ -1860,7 +1860,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
: _("could not apply %s... %s"),
short_commit_name(commit), msg.subject);
print_advice(res == 1, opts);
- rerere(opts->allow_rerere_auto);
+ repo_rerere(the_repository, opts->allow_rerere_auto);
goto leave;
}
@@ -1913,7 +1913,7 @@ static int read_and_refresh_cache(struct replay_opts *opts)
{
struct lock_file index_lock = LOCK_INIT;
int index_fd = hold_locked_index(&index_lock, 0);
- if (read_index_preload(&the_index, NULL) < 0) {
+ if (read_index_preload(&the_index, NULL, 0) < 0) {
rollback_lock_file(&index_lock);
return error(_("git %s: failed to read the index"),
_(action_name(opts)));
@@ -2599,7 +2599,7 @@ static int make_patch(struct commit *commit, struct replay_opts *opts)
strbuf_addf(&buf, "%s/patch", get_dir(opts));
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
- init_revisions(&log_tree_opt, NULL);
+ repo_init_revisions(the_repository, &log_tree_opt, NULL);
log_tree_opt.abbrev = 0;
log_tree_opt.diff = 1;
log_tree_opt.diffopt.output_format = DIFF_FORMAT_PATCH;
@@ -3179,7 +3179,7 @@ static int do_merge(struct commit *commit, const char *arg, int arg_len,
rollback_lock_file(&lock);
if (ret)
- rerere(opts->allow_rerere_auto);
+ repo_rerere(the_repository, opts->allow_rerere_auto);
else
/*
* In case of problems, we now want to return a positive
@@ -3510,7 +3510,7 @@ cleanup_head_ref:
struct object_id orig, head;
memset(&log_tree_opt, 0, sizeof(log_tree_opt));
- init_revisions(&log_tree_opt, NULL);
+ repo_init_revisions(the_repository, &log_tree_opt, NULL);
log_tree_opt.diff = 1;
log_tree_opt.diffopt.output_format =
DIFF_FORMAT_DIFFSTAT;
@@ -4146,9 +4146,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
struct object_id *oid = &parent->item->object.oid;
if (!oidset_contains(&interesting, oid))
continue;
- if (!oidset_contains(&child_seen, oid))
- oidset_insert(&child_seen, oid);
- else
+ if (oidset_insert(&child_seen, oid))
label_oid(oid, "branch-point", &state);
}
@@ -4256,7 +4254,7 @@ int sequencer_make_script(FILE *out, int argc, const char **argv,
const char *insn = flags & TODO_LIST_ABBREVIATE_CMDS ? "p" : "pick";
int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
revs.verbose_header = 1;
if (!rebase_merges)
revs.max_parents = 1;
diff --git a/sha1-file.c b/sha1-file.c
index a4367b8f04..dd0b6aa873 100644
--- a/sha1-file.c
+++ b/sha1-file.c
@@ -1813,7 +1813,8 @@ static void check_tag(const void *buf, size_t size)
die(_("corrupt tag"));
}
-static int index_mem(struct object_id *oid, void *buf, size_t size,
+static int index_mem(struct index_state *istate,
+ struct object_id *oid, void *buf, size_t size,
enum object_type type,
const char *path, unsigned flags)
{
@@ -1828,7 +1829,7 @@ static int index_mem(struct object_id *oid, void *buf, size_t size,
*/
if ((type == OBJ_BLOB) && path) {
struct strbuf nbuf = STRBUF_INIT;
- if (convert_to_git(&the_index, path, buf, size, &nbuf,
+ if (convert_to_git(istate, path, buf, size, &nbuf,
get_conv_flags(flags))) {
buf = strbuf_detach(&nbuf, &size);
re_allocated = 1;
@@ -1852,17 +1853,20 @@ static int index_mem(struct object_id *oid, void *buf, size_t size,
return ret;
}
-static int index_stream_convert_blob(struct object_id *oid, int fd,
- const char *path, unsigned flags)
+static int index_stream_convert_blob(struct index_state *istate,
+ struct object_id *oid,
+ int fd,
+ const char *path,
+ unsigned flags)
{
int ret;
const int write_object = flags & HASH_WRITE_OBJECT;
struct strbuf sbuf = STRBUF_INIT;
assert(path);
- assert(would_convert_to_git_filter_fd(&the_index, path));
+ assert(would_convert_to_git_filter_fd(istate, path));
- convert_to_git_filter_fd(&the_index, path, fd, &sbuf,
+ convert_to_git_filter_fd(istate, path, fd, &sbuf,
get_conv_flags(flags));
if (write_object)
@@ -1875,14 +1879,15 @@ static int index_stream_convert_blob(struct object_id *oid, int fd,
return ret;
}
-static int index_pipe(struct object_id *oid, int fd, enum object_type type,
+static int index_pipe(struct index_state *istate, struct object_id *oid,
+ int fd, enum object_type type,
const char *path, unsigned flags)
{
struct strbuf sbuf = STRBUF_INIT;
int ret;
if (strbuf_read(&sbuf, fd, 4096) >= 0)
- ret = index_mem(oid, sbuf.buf, sbuf.len, type, path, flags);
+ ret = index_mem(istate, oid, sbuf.buf, sbuf.len, type, path, flags);
else
ret = -1;
strbuf_release(&sbuf);
@@ -1891,14 +1896,15 @@ static int index_pipe(struct object_id *oid, int fd, enum object_type type,
#define SMALL_FILE_SIZE (32*1024)
-static int index_core(struct object_id *oid, int fd, size_t size,
+static int index_core(struct index_state *istate,
+ struct object_id *oid, int fd, size_t size,
enum object_type type, const char *path,
unsigned flags)
{
int ret;
if (!size) {
- ret = index_mem(oid, "", size, type, path, flags);
+ ret = index_mem(istate, oid, "", size, type, path, flags);
} else if (size <= SMALL_FILE_SIZE) {
char *buf = xmalloc(size);
ssize_t read_result = read_in_full(fd, buf, size);
@@ -1909,11 +1915,11 @@ static int index_core(struct object_id *oid, int fd, size_t size,
ret = error(_("short read while indexing %s"),
path ? path : "<unknown>");
else
- ret = index_mem(oid, buf, size, type, path, flags);
+ ret = index_mem(istate, oid, buf, size, type, path, flags);
free(buf);
} else {
void *buf = xmmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
- ret = index_mem(oid, buf, size, type, path, flags);
+ ret = index_mem(istate, oid, buf, size, type, path, flags);
munmap(buf, size);
}
return ret;
@@ -1941,7 +1947,8 @@ static int index_stream(struct object_id *oid, int fd, size_t size,
return index_bulk_checkin(oid, fd, size, type, path, flags);
}
-int index_fd(struct object_id *oid, int fd, struct stat *st,
+int index_fd(struct index_state *istate, struct object_id *oid,
+ int fd, struct stat *st,
enum object_type type, const char *path, unsigned flags)
{
int ret;
@@ -1950,14 +1957,14 @@ int index_fd(struct object_id *oid, int fd, struct stat *st,
* Call xsize_t() only when needed to avoid potentially unnecessary
* die() for large files.
*/
- if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(&the_index, path))
- ret = index_stream_convert_blob(oid, fd, path, flags);
+ if (type == OBJ_BLOB && path && would_convert_to_git_filter_fd(istate, path))
+ ret = index_stream_convert_blob(istate, oid, fd, path, flags);
else if (!S_ISREG(st->st_mode))
- ret = index_pipe(oid, fd, type, path, flags);
+ ret = index_pipe(istate, oid, fd, type, path, flags);
else if (st->st_size <= big_file_threshold || type != OBJ_BLOB ||
- (path && would_convert_to_git(&the_index, path)))
- ret = index_core(oid, fd, xsize_t(st->st_size), type, path,
- flags);
+ (path && would_convert_to_git(istate, path)))
+ ret = index_core(istate, oid, fd, xsize_t(st->st_size),
+ type, path, flags);
else
ret = index_stream(oid, fd, xsize_t(st->st_size), type, path,
flags);
@@ -1965,7 +1972,8 @@ int index_fd(struct object_id *oid, int fd, struct stat *st,
return ret;
}
-int index_path(struct object_id *oid, const char *path, struct stat *st, unsigned flags)
+int index_path(struct index_state *istate, struct object_id *oid,
+ const char *path, struct stat *st, unsigned flags)
{
int fd;
struct strbuf sb = STRBUF_INIT;
@@ -1976,7 +1984,7 @@ int index_path(struct object_id *oid, const char *path, struct stat *st, unsigne
fd = open(path, O_RDONLY);
if (fd < 0)
return error_errno("open(\"%s\")", path);
- if (index_fd(oid, fd, st, OBJ_BLOB, path, flags) < 0)
+ if (index_fd(istate, oid, fd, st, OBJ_BLOB, path, flags) < 0)
return error(_("%s: failed to insert into database"),
path);
break;
diff --git a/shallow.c b/shallow.c
index 99fd2d1ba0..732e18d54f 100644
--- a/shallow.c
+++ b/shallow.c
@@ -185,7 +185,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
is_repository_shallow(the_repository); /* make sure shallows are read */
- init_revisions(&revs, NULL);
+ repo_init_revisions(the_repository, &revs, NULL);
save_commit_buffer = 0;
setup_revisions(ac, av, &revs, NULL);
diff --git a/split-index.c b/split-index.c
index 84f067e10d..5820412dc5 100644
--- a/split-index.c
+++ b/split-index.c
@@ -111,7 +111,7 @@ static void mark_entry_for_delete(size_t pos, void *data)
die("position for delete %d exceeds base index size %d",
(int)pos, istate->cache_nr);
istate->cache[pos]->ce_flags |= CE_REMOVE;
- istate->split_index->nr_deletions = 1;
+ istate->split_index->nr_deletions++;
}
static void replace_entry(size_t pos, void *data)
@@ -188,6 +188,30 @@ void merge_base_index(struct index_state *istate)
si->saved_cache_nr = 0;
}
+/*
+ * Compare most of the fields in two cache entries, i.e. all except the
+ * hashmap_entry and the name.
+ */
+static int compare_ce_content(struct cache_entry *a, struct cache_entry *b)
+{
+ const unsigned int ondisk_flags = CE_STAGEMASK | CE_VALID |
+ CE_EXTENDED_FLAGS;
+ unsigned int ce_flags = a->ce_flags;
+ unsigned int base_flags = b->ce_flags;
+ int ret;
+
+ /* only on-disk flags matter */
+ a->ce_flags &= ondisk_flags;
+ b->ce_flags &= ondisk_flags;
+ ret = memcmp(&a->ce_stat_data, &b->ce_stat_data,
+ offsetof(struct cache_entry, name) -
+ offsetof(struct cache_entry, ce_stat_data));
+ a->ce_flags = ce_flags;
+ b->ce_flags = base_flags;
+
+ return ret;
+}
+
void prepare_to_write_split_index(struct index_state *istate)
{
struct split_index *si = init_split_index(istate);
@@ -207,38 +231,109 @@ void prepare_to_write_split_index(struct index_state *istate)
*/
for (i = 0; i < istate->cache_nr; i++) {
struct cache_entry *base;
- /* namelen is checked separately */
- const unsigned int ondisk_flags =
- CE_STAGEMASK | CE_VALID | CE_EXTENDED_FLAGS;
- unsigned int ce_flags, base_flags, ret;
ce = istate->cache[i];
- if (!ce->index)
+ if (!ce->index) {
+ /*
+ * During simple update index operations this
+ * is a cache entry that is not present in
+ * the shared index. It will be added to the
+ * split index.
+ *
+ * However, it might also represent a file
+ * that already has a cache entry in the
+ * shared index, but a new index has just
+ * been constructed by unpack_trees(), and
+ * this entry now refers to different content
+ * than what was recorded in the original
+ * index, e.g. during 'read-tree -m HEAD^' or
+ * 'checkout HEAD^'. In this case the
+ * original entry in the shared index will be
+ * marked as deleted, and this entry will be
+ * added to the split index.
+ */
continue;
+ }
if (ce->index > si->base->cache_nr) {
- ce->index = 0;
- continue;
+ BUG("ce refers to a shared ce at %d, which is beyond the shared index size %d",
+ ce->index, si->base->cache_nr);
}
ce->ce_flags |= CE_MATCHED; /* or "shared" */
base = si->base->cache[ce->index - 1];
- if (ce == base)
+ if (ce == base) {
+ /* The entry is present in the shared index. */
+ if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+ /*
+ * Already marked for inclusion in
+ * the split index, either because
+ * the corresponding file was
+ * modified and the cached stat data
+ * was refreshed, or because there
+ * is already a replacement entry in
+ * the split index.
+ * Nothing more to do here.
+ */
+ } else if (!ce_uptodate(ce) &&
+ is_racy_timestamp(istate, ce)) {
+ /*
+ * A racily clean cache entry stored
+ * only in the shared index: it must
+ * be added to the split index, so
+ * the subsequent do_write_index()
+ * can smudge its stat data.
+ */
+ ce->ce_flags |= CE_UPDATE_IN_BASE;
+ } else {
+ /*
+ * The entry is only present in the
+ * shared index and it was not
+ * refreshed.
+ * Just leave it there.
+ */
+ }
continue;
+ }
if (ce->ce_namelen != base->ce_namelen ||
strcmp(ce->name, base->name)) {
ce->index = 0;
continue;
}
- ce_flags = ce->ce_flags;
- base_flags = base->ce_flags;
- /* only on-disk flags matter */
- ce->ce_flags &= ondisk_flags;
- base->ce_flags &= ondisk_flags;
- ret = memcmp(&ce->ce_stat_data, &base->ce_stat_data,
- offsetof(struct cache_entry, name) -
- offsetof(struct cache_entry, ce_stat_data));
- ce->ce_flags = ce_flags;
- base->ce_flags = base_flags;
- if (ret)
+ /*
+ * This is the copy of a cache entry that is present
+ * in the shared index, created by unpack_trees()
+ * while it constructed a new index.
+ */
+ if (ce->ce_flags & CE_UPDATE_IN_BASE) {
+ /*
+ * Already marked for inclusion in the split
+ * index, either because the corresponding
+ * file was modified and the cached stat data
+ * was refreshed, or because the original
+ * entry already had a replacement entry in
+ * the split index.
+ * Nothing to do.
+ */
+ } else if (!ce_uptodate(ce) &&
+ is_racy_timestamp(istate, ce)) {
+ /*
+ * A copy of a racily clean cache entry from
+ * the shared index. It must be added to
+ * the split index, so the subsequent
+ * do_write_index() can smudge its stat data.
+ */
ce->ce_flags |= CE_UPDATE_IN_BASE;
+ } else {
+ /*
+ * Thoroughly compare the cached data to see
+ * whether it should be marked for inclusion
+ * in the split index.
+ *
+ * This comparison might be unnecessary, as
+ * code paths modifying the cached data do
+ * set CE_UPDATE_IN_BASE as well.
+ */
+ if (compare_ce_content(ce, base))
+ ce->ce_flags |= CE_UPDATE_IN_BASE;
+ }
discard_cache_entry(base);
si->base->cache[ce->index - 1] = ce;
}
diff --git a/submodule.c b/submodule.c
index b53cb6e9c4..d9d3046689 100644
--- a/submodule.c
+++ b/submodule.c
@@ -428,7 +428,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
{
struct commit_list *list;
- init_revisions(rev, NULL);
+ repo_init_revisions(the_repository, rev, NULL);
setup_revisions(0, NULL, rev, NULL);
rev->left_right = 1;
rev->first_parent_only = 1;
@@ -766,13 +766,14 @@ static void collect_changed_submodules_cb(struct diff_queue_struct *q,
* have a corresponding 'struct oid_array' (in the 'util' field) which lists
* what the submodule pointers were updated to during the change.
*/
-static void collect_changed_submodules(struct string_list *changed,
+static void collect_changed_submodules(struct index_state *istate,
+ struct string_list *changed,
struct argv_array *argv)
{
struct rev_info rev;
const struct commit *commit;
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
setup_revisions(argv->argc, argv->argv, &rev, NULL);
if (prepare_revision_walk(&rev))
die("revision walk setup failed");
@@ -783,7 +784,7 @@ static void collect_changed_submodules(struct string_list *changed,
data.changed = changed;
data.commit_oid = &commit->object.oid;
- init_revisions(&diff_rev, NULL);
+ repo_init_revisions(the_repository, &diff_rev, NULL);
diff_rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
diff_rev.diffopt.format_callback = collect_changed_submodules_cb;
diff_rev.diffopt.format_callback_data = &data;
@@ -930,8 +931,10 @@ static int submodule_needs_pushing(const char *path, struct oid_array *commits)
return 0;
}
-int find_unpushed_submodules(struct oid_array *commits,
- const char *remotes_name, struct string_list *needs_pushing)
+int find_unpushed_submodules(struct index_state *istate,
+ struct oid_array *commits,
+ const char *remotes_name,
+ struct string_list *needs_pushing)
{
struct string_list submodules = STRING_LIST_INIT_DUP;
struct string_list_item *name;
@@ -943,7 +946,7 @@ int find_unpushed_submodules(struct oid_array *commits,
argv_array_push(&argv, "--not");
argv_array_pushf(&argv, "--remotes=%s", remotes_name);
- collect_changed_submodules(&submodules, &argv);
+ collect_changed_submodules(istate, &submodules, &argv);
for_each_string_list_item(name, &submodules) {
struct oid_array *commits = name->util;
@@ -1044,7 +1047,8 @@ static void submodule_push_check(const char *path, const char *head,
die("process for submodule '%s' failed", path);
}
-int push_unpushed_submodules(struct oid_array *commits,
+int push_unpushed_submodules(struct index_state *istate,
+ struct oid_array *commits,
const struct remote *remote,
const struct refspec *rs,
const struct string_list *push_options,
@@ -1053,7 +1057,8 @@ int push_unpushed_submodules(struct oid_array *commits,
int i, ret = 1;
struct string_list needs_pushing = STRING_LIST_INIT_DUP;
- if (!find_unpushed_submodules(commits, remote->name, &needs_pushing))
+ if (!find_unpushed_submodules(istate, commits,
+ remote->name, &needs_pushing))
return 1;
/*
@@ -1110,7 +1115,7 @@ void check_for_new_submodule_commits(struct object_id *oid)
oid_array_append(&ref_tips_after_fetch, oid);
}
-static void calculate_changed_submodule_paths(void)
+static void calculate_changed_submodule_paths(struct index_state *istate)
{
struct argv_array argv = ARGV_ARRAY_INIT;
struct string_list changed_submodules = STRING_LIST_INIT_DUP;
@@ -1131,7 +1136,7 @@ static void calculate_changed_submodule_paths(void)
* Collect all submodules (whether checked out or not) for which new
* commits have been recorded upstream in "changed_submodule_names".
*/
- collect_changed_submodules(&changed_submodules, &argv);
+ collect_changed_submodules(istate, &changed_submodules, &argv);
for_each_string_list_item(name, &changed_submodules) {
struct oid_array *commits = name->util;
@@ -1158,7 +1163,8 @@ static void calculate_changed_submodule_paths(void)
initialized_fetch_ref_tips = 0;
}
-int submodule_touches_in_range(struct object_id *excl_oid,
+int submodule_touches_in_range(struct index_state *istate,
+ struct object_id *excl_oid,
struct object_id *incl_oid)
{
struct string_list subs = STRING_LIST_INIT_DUP;
@@ -1176,7 +1182,7 @@ int submodule_touches_in_range(struct object_id *excl_oid,
argv_array_push(&args, oid_to_hex(excl_oid));
}
- collect_changed_submodules(&subs, &args);
+ collect_changed_submodules(istate, &subs, &args);
ret = subs.nr;
argv_array_clear(&args);
@@ -1346,7 +1352,7 @@ int fetch_populated_submodules(struct repository *r,
argv_array_push(&spf.args, "--recurse-submodules-default");
/* default value, "--submodule-prefix" and its value are added later */
- calculate_changed_submodule_paths();
+ calculate_changed_submodule_paths(r->index);
run_processes_parallel(max_parallel_jobs,
get_next_submodule,
fetch_start_failure,
@@ -1880,7 +1886,7 @@ const char *get_superproject_working_tree(void)
* We're only interested in the name after the tab.
*/
super_sub = strchr(sb.buf, '\t') + 1;
- super_sub_len = sb.buf + sb.len - super_sub - 1;
+ super_sub_len = strlen(super_sub);
if (super_sub_len > cwd_len ||
strcmp(&cwd[cwd_len - super_sub_len], super_sub))
diff --git a/submodule.h b/submodule.h
index e452919aa4..4826601ff2 100644
--- a/submodule.h
+++ b/submodule.h
@@ -102,13 +102,16 @@ int add_submodule_odb(const char *path);
* Checks if there are submodule changes in a..b. If a is the null OID,
* checks b and all its ancestors instead.
*/
-int submodule_touches_in_range(struct object_id *a,
+int submodule_touches_in_range(struct index_state *istate,
+ struct object_id *a,
struct object_id *b);
-int find_unpushed_submodules(struct oid_array *commits,
+int find_unpushed_submodules(struct index_state *istate,
+ struct oid_array *commits,
const char *remotes_name,
struct string_list *needs_pushing);
struct refspec;
-int push_unpushed_submodules(struct oid_array *commits,
+int push_unpushed_submodules(struct index_state *istate,
+ struct oid_array *commits,
const struct remote *remote,
const struct refspec *rs,
const struct string_list *push_options,
diff --git a/t/README b/t/README
index 5e48a043ce..8847489640 100644
--- a/t/README
+++ b/t/README
@@ -315,7 +315,7 @@ packs on demand. This normally only happens when the object size is
over 2GB. This variable forces the code path on any object larger than
<n> bytes.
-GIT_TEST_OE_DELTA_SIZE=<n> exercises the uncomon pack-objects code
+GIT_TEST_OE_DELTA_SIZE=<n> exercises the uncommon pack-objects code
path where deltas larger than this limit require extra memory
allocation for bookkeeping.
@@ -327,6 +327,22 @@ GIT_TEST_COMMIT_GRAPH=<boolean>, when true, forces the commit-graph to
be written after every 'git commit' command, and overrides the
'core.commitGraph' setting to true.
+GIT_TEST_FSMONITOR=$PWD/t7519/fsmonitor-all exercises the fsmonitor
+code path for utilizing a file system monitor to speed up detecting
+new or changed files.
+
+GIT_TEST_INDEX_VERSION=<n> exercises the index read/write code path
+for the index version specified. Can be set to any valid version
+(currently 2, 3, or 4).
+
+GIT_TEST_PRELOAD_INDEX=<boolean> exercises the preload-index code path
+by overriding the minimum number of cache entries required per thread.
+
+GIT_TEST_INDEX_THREADS=<n> enables exercising the multi-threaded loading
+of the index for the whole test suite by bypassing the default number of
+cache entries and thread minimums. Setting this to 1 will make the
+index loading single threaded.
+
Naming Tests
------------
@@ -401,13 +417,13 @@ This test harness library does the following things:
consistently when command line arguments --verbose (or -v),
--debug (or -d), and --immediate (or -i) is given.
-Do's, don'ts & things to keep in mind
--------------------------------------
+Do's & don'ts
+-------------
Here are a few examples of things you probably should and shouldn't do
when writing tests.
-Do:
+Here are the "do's:"
- Put all code inside test_expect_success and other assertions.
@@ -452,16 +468,21 @@ Do:
Windows, where the shell (MSYS bash) mangles absolute path names.
For details, see the commit message of 4114156ae9.
-Don't:
+ - Remember that inside the <script> part, the standard output and
+ standard error streams are discarded, and the test harness only
+ reports "ok" or "not ok" to the end user running the tests. Under
+ --verbose, they are shown to help debug the tests.
- - exit() within a <script> part.
+And here are the "don'ts:"
+
+ - Don't exit() within a <script> part.
The harness will catch this as a programming error of the test.
Use test_done instead if you need to stop the tests early (see
"Skipping tests" below).
- - use '! git cmd' when you want to make sure the git command exits
- with failure in a controlled way by calling "die()". Instead,
+ - Don't use '! git cmd' when you want to make sure the git command
+ exits with failure in a controlled way by calling "die()". Instead,
use 'test_must_fail git cmd'. This will signal a failure if git
dies in an unexpected way (e.g. segfault).
@@ -469,8 +490,35 @@ Don't:
platform commands; just use '! cmd'. We are not in the business
of verifying that the world given to us sanely works.
- - use perl without spelling it as "$PERL_PATH". This is to help our
- friends on Windows where the platform Perl often adds CR before
+ - Don't feed the output of a git command to a pipe, as in:
+
+ git -C repo ls-files |
+ xargs -n 1 basename |
+ grep foo
+
+ which will discard git's exit code and may mask a crash. In the
+ above example, all exit codes are ignored except grep's.
+
+ Instead, write the output of that command to a temporary
+ file with ">" or assign it to a variable with "x=$(git ...)" rather
+ than pipe it.
+
+ - Don't use command substitution in a way that discards git's exit
+ code. When assigning to a variable, the exit code is not discarded,
+ e.g.:
+
+ x=$(git cat-file -p $sha) &&
+ ...
+
+ is OK because a crash in "git cat-file" will cause the "&&" chain
+ to fail, but:
+
+ test "refs/heads/foo" = "$(git symbolic-ref HEAD)"
+
+ is not OK and a crash in git could go undetected.
+
+ - Don't use perl without spelling it as "$PERL_PATH". This is to help
+ our friends on Windows where the platform Perl often adds CR before
the end of line, and they bundle Git with a version of Perl that
does not do so, whose path is specified with $PERL_PATH. Note that we
provide a "perl" function which uses $PERL_PATH under the hood, so
@@ -478,17 +526,17 @@ Don't:
(but you do, for example, on a shebang line or in a sub script
created via "write_script").
- - use sh without spelling it as "$SHELL_PATH", when the script can
- be misinterpreted by broken platform shell (e.g. Solaris).
+ - Don't use sh without spelling it as "$SHELL_PATH", when the script
+ can be misinterpreted by broken platform shell (e.g. Solaris).
- - chdir around in tests. It is not sufficient to chdir to
+ - Don't chdir around in tests. It is not sufficient to chdir to
somewhere and then chdir back to the original location later in
the test, as any intermediate step can fail and abort the test,
causing the next test to start in an unexpected directory. Do so
inside a subshell if necessary.
- - save and verify the standard error of compound commands, i.e. group
- commands, subshells, and shell functions (except test helper
+ - Don't save and verify the standard error of compound commands, i.e.
+ group commands, subshells, and shell functions (except test helper
functions like 'test_must_fail') like this:
( cd dir && git cmd ) 2>error &&
@@ -503,7 +551,7 @@ Don't:
( cd dir && git cmd 2>../error ) &&
test_cmp expect error
- - Break the TAP output
+ - Don't break the TAP output
The raw output from your test may be interpreted by a TAP harness. TAP
harnesses will ignore everything they don't know about, but don't step
@@ -523,13 +571,6 @@ Don't:
but the best indication is to just run the tests with prove(1),
it'll complain if anything is amiss.
-Keep in mind:
-
- - Inside the <script> part, the standard output and standard error
- streams are discarded, and the test harness only reports "ok" or
- "not ok" to the end user running the tests. Under --verbose, they
- are shown to help debugging the tests.
-
Skipping tests
--------------
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 2762ca6562..6a84a53efb 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -15,7 +15,10 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
struct commit *c;
struct commit_list *parent;
- repo_init(&r, gitdir, worktree);
+ setup_git_env(gitdir);
+
+ if (repo_init(&r, gitdir, worktree))
+ die("Couldn't init repo");
c = lookup_commit(&r, commit_oid);
@@ -38,7 +41,10 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
struct commit *c;
struct tree *tree;
- repo_init(&r, gitdir, worktree);
+ setup_git_env(gitdir);
+
+ if (repo_init(&r, gitdir, worktree))
+ die("Couldn't init repo");
c = lookup_commit(&r, commit_oid);
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 4f8bc75821..625b2dbf82 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -32,7 +32,7 @@ static int run_revision_walk(void)
int argc = ARRAY_SIZE(argv) - 1;
int got_revision = 0;
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
setup_revisions(argc, argv, &rev, NULL);
if (prepare_revision_walk(&rev))
die("revision walk setup failed");
diff --git a/t/lib-gpg.sh b/t/lib-gpg.sh
index 3fe02876c1..f1277bef4f 100755
--- a/t/lib-gpg.sh
+++ b/t/lib-gpg.sh
@@ -57,9 +57,12 @@ then
echo | gpgsm --homedir "${GNUPGHOME}" 2>/dev/null \
--passphrase-fd 0 --pinentry-mode loopback \
--import "$TEST_DIRECTORY"/lib-gpg/gpgsm_cert.p12 &&
- gpgsm --homedir "${GNUPGHOME}" 2>/dev/null -K \
- | grep fingerprint: | cut -d" " -f4 | tr -d '\n' > \
- ${GNUPGHOME}/trustlist.txt &&
+
+ gpgsm --homedir "${GNUPGHOME}" 2>/dev/null -K |
+ grep fingerprint: |
+ cut -d" " -f4 |
+ tr -d '\n' >"${GNUPGHOME}/trustlist.txt" &&
+
echo " S relax" >> ${GNUPGHOME}/trustlist.txt &&
(gpgconf --kill gpg-agent >/dev/null 2>&1 || : ) &&
echo hello | gpgsm --homedir "${GNUPGHOME}" >/dev/null \
diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh
index 391f910c6a..4d23373526 100755
--- a/t/t0000-basic.sh
+++ b/t/t0000-basic.sh
@@ -87,6 +87,10 @@ _run_sub_test_lib_test_common () {
passing metrics
'
+ # Tell the framework that we are self-testing to make sure
+ # it yields a stable result.
+ GIT_TEST_FRAMEWORK_SELFTEST=t &&
+
# Point to the t/test-lib.sh, which isn't in ../ as usual
. "\$TEST_DIRECTORY"/test-lib.sh
EOF
@@ -1097,7 +1101,7 @@ test_expect_success 'validate git diff-files output for a know cache/work tree s
:120000 120000 $(test_oid subp3s) $ZERO_OID M path3/subp3/file3sym
EOF
git diff-files >current &&
- test_cmp current expected
+ test_cmp expected current
'
test_expect_success 'git update-index --refresh should succeed' '
diff --git a/t/t0012-help.sh b/t/t0012-help.sh
index bc27df7f38..e8ef7300ec 100755
--- a/t/t0012-help.sh
+++ b/t/t0012-help.sh
@@ -29,9 +29,9 @@ test_expect_success "setup" '
# to verify
test_expect_success 'basic help commands' '
git help >/dev/null &&
- git help -a >/dev/null &&
+ git help -a --no-verbose >/dev/null &&
git help -g >/dev/null &&
- git help -av >/dev/null
+ git help -a >/dev/null
'
test_expect_success "works for commands and guides by default" '
diff --git a/t/t0014-alias.sh b/t/t0014-alias.sh
new file mode 100755
index 0000000000..a070e645d7
--- /dev/null
+++ b/t/t0014-alias.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+
+test_description='git command aliasing'
+
+. ./test-lib.sh
+
+test_expect_success 'nested aliases - internal execution' '
+ git config alias.nested-internal-1 nested-internal-2 &&
+ git config alias.nested-internal-2 status &&
+ git nested-internal-1 >output &&
+ test_i18ngrep "^On branch " output
+'
+
+test_expect_success 'nested aliases - mixed execution' '
+ git config alias.nested-external-1 nested-external-2 &&
+ git config alias.nested-external-2 "!git nested-external-3" &&
+ git config alias.nested-external-3 status &&
+ git nested-external-1 >output &&
+ test_i18ngrep "^On branch " output
+'
+
+test_expect_success 'looping aliases - internal execution' '
+ git config alias.loop-internal-1 loop-internal-2 &&
+ git config alias.loop-internal-2 loop-internal-3 &&
+ git config alias.loop-internal-3 loop-internal-2 &&
+ test_must_fail git loop-internal-1 2>output &&
+ test_i18ngrep "^fatal: alias loop detected: expansion of" output
+'
+
+# This test is disabled until external loops are fixed, because would block
+# the test suite for a full minute.
+#
+#test_expect_failure 'looping aliases - mixed execution' '
+# git config alias.loop-mixed-1 loop-mixed-2 &&
+# git config alias.loop-mixed-2 "!git loop-mixed-1" &&
+# test_must_fail git loop-mixed-1 2>output &&
+# test_i18ngrep "^fatal: alias loop detected: expansion of" output
+#'
+
+test_done
diff --git a/t/t0021-conversion.sh b/t/t0021-conversion.sh
index 308cd28f3b..fd5f1ac649 100755
--- a/t/t0021-conversion.sh
+++ b/t/t0021-conversion.sh
@@ -166,10 +166,10 @@ test_expect_success expanded_in_repo '
rm -f expanded-keywords expanded-keywords-crlf &&
git checkout -- expanded-keywords &&
- test_cmp expanded-keywords expected-output &&
+ test_cmp expected-output expanded-keywords &&
git checkout -- expanded-keywords-crlf &&
- test_cmp expanded-keywords-crlf expected-output-crlf
+ test_cmp expected-output-crlf expanded-keywords-crlf
'
# The use of %f in a filter definition is expanded to the path to
diff --git a/t/t0410-partial-clone.sh b/t/t0410-partial-clone.sh
index cfd0655ea1..c521d7d6c6 100755
--- a/t/t0410-partial-clone.sh
+++ b/t/t0410-partial-clone.sh
@@ -182,6 +182,47 @@ 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 blobs works' '
+ rm -rf server repo &&
+ test_create_repo server &&
+ test_commit -C server foo &&
+ git -C server repack -a -d --write-bitmap-index &&
+
+ git clone "file://$(pwd)/server" repo &&
+ git hash-object repo/foo.t >blobhash &&
+ rm -rf repo/.git/objects/* &&
+
+ git -C server config uploadpack.allowanysha1inwant 1 &&
+ git -C server config uploadpack.allowfilter 1 &&
+ git -C repo config core.repositoryformatversion 1 &&
+ git -C repo config extensions.partialclone "origin" &&
+
+ git -C repo cat-file -p $(cat blobhash)
+'
+
+test_expect_success 'fetching of missing trees does not fetch blobs' '
+ rm -rf server repo &&
+ test_create_repo server &&
+ test_commit -C server foo &&
+ git -C server repack -a -d --write-bitmap-index &&
+
+ git clone "file://$(pwd)/server" repo &&
+ git -C repo rev-parse foo^{tree} >treehash &&
+ git hash-object repo/foo.t >blobhash &&
+ rm -rf repo/.git/objects/* &&
+
+ git -C server config uploadpack.allowanysha1inwant 1 &&
+ git -C server config uploadpack.allowfilter 1 &&
+ git -C repo config core.repositoryformatversion 1 &&
+ git -C repo config extensions.partialclone "origin" &&
+ git -C repo cat-file -p $(cat treehash) &&
+
+ # Ensure that the tree, but not the blob, is fetched
+ git -C repo rev-list --objects --missing=print $(cat treehash) >objects &&
+ grep "^$(cat treehash)" objects &&
+ grep "^[?]$(cat blobhash)" objects
+'
+
test_expect_success 'rev-list stops traversal at missing and promised commit' '
rm -rf repo &&
test_create_repo repo &&
diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh
index a7c95bb785..43c4be1e5e 100755
--- a/t/t1006-cat-file.sh
+++ b/t/t1006-cat-file.sh
@@ -220,8 +220,8 @@ test_expect_success "--batch-check for a non-existent hash" '
test "0000000000000000000000000000000000000042 missing
0000000000000000000000000000000000000084 missing" = \
"$( ( echo 0000000000000000000000000000000000000042;
- echo_without_newline 0000000000000000000000000000000000000084; ) \
- | git cat-file --batch-check)"
+ echo_without_newline 0000000000000000000000000000000000000084; ) |
+ git cat-file --batch-check)"
'
test_expect_success "--batch for an existent and a non-existent hash" '
@@ -229,8 +229,8 @@ test_expect_success "--batch for an existent and a non-existent hash" '
$tag_content
0000000000000000000000000000000000000000 missing" = \
"$( ( echo $tag_sha1;
- echo_without_newline 0000000000000000000000000000000000000000; ) \
- | git cat-file --batch)"
+ echo_without_newline 0000000000000000000000000000000000000000; ) |
+ git cat-file --batch)"
'
test_expect_success "--batch-check for an empty line" '
diff --git a/t/t1090-sparse-checkout-scope.sh b/t/t1090-sparse-checkout-scope.sh
index 25d7c700f6..090b7fc3d3 100755
--- a/t/t1090-sparse-checkout-scope.sh
+++ b/t/t1090-sparse-checkout-scope.sh
@@ -63,4 +63,37 @@ test_expect_success 'return to full checkout of master' '
test "$(cat b)" = "modified"
'
+test_expect_success 'in partial clone, sparse checkout only fetches needed blobs' '
+ test_create_repo server &&
+ git clone "file://$(pwd)/server" client &&
+
+ test_config -C server uploadpack.allowfilter 1 &&
+ test_config -C server uploadpack.allowanysha1inwant 1 &&
+ echo a >server/a &&
+ echo bb >server/b &&
+ mkdir server/c &&
+ echo ccc >server/c/c &&
+ git -C server add a b c/c &&
+ git -C server commit -m message &&
+
+ test_config -C client core.sparsecheckout 1 &&
+ test_config -C client extensions.partialclone origin &&
+ echo "!/*" >client/.git/info/sparse-checkout &&
+ echo "/a" >>client/.git/info/sparse-checkout &&
+ git -C client fetch --filter=blob:none origin &&
+ git -C client checkout FETCH_HEAD &&
+
+ git -C client rev-list HEAD \
+ --quiet --objects --missing=print >unsorted_actual &&
+ (
+ printf "?" &&
+ git hash-object server/b &&
+ printf "?" &&
+ git hash-object server/c/c
+ ) >unsorted_expect &&
+ sort unsorted_actual >actual &&
+ sort unsorted_expect >expect &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t1300-config.sh b/t/t1300-config.sh
index cdf1fed5d1..e2cd50ecfc 100755
--- a/t/t1300-config.sh
+++ b/t/t1300-config.sh
@@ -1001,7 +1001,7 @@ EOF
test_expect_success 'value continued on next line' '
git config --list > result &&
- test_cmp result expect
+ test_cmp expect result
'
cat > .git/config <<\EOF
@@ -1770,8 +1770,9 @@ test_expect_success '--show-origin stdin with file include' '
cat >expect <<-EOF &&
file:$INCLUDE_DIR/stdin.include include
EOF
- echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" \
- | git config --show-origin --includes --file - user.stdin >output &&
+ echo "[include]path=\"$INCLUDE_DIR\"/stdin.include" |
+ git config --show-origin --includes --file - user.stdin >output &&
+
test_cmp expect output
'
@@ -1881,7 +1882,7 @@ test_expect_success '--replace-all does not invent newlines' '
Qkey = b
EOF
git config --replace-all abc.key b &&
- test_cmp .git/config expect
+ test_cmp expect .git/config
'
test_done
diff --git a/t/t1303-wacky-config.sh b/t/t1303-wacky-config.sh
index 3b92083e19..0000e664e7 100755
--- a/t/t1303-wacky-config.sh
+++ b/t/t1303-wacky-config.sh
@@ -14,7 +14,7 @@ setup() {
check() {
echo "$2" >expected
git config --get "$1" >actual 2>&1
- test_cmp actual expected
+ test_cmp expected actual
}
# 'check section.key regex value' verifies that the entry for
@@ -22,7 +22,7 @@ check() {
check_regex() {
echo "$3" >expected
git config --get "$1" "$2" >actual 2>&1
- test_cmp actual expected
+ test_cmp expected actual
}
test_expect_success 'modify same key' '
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 663f17c5fe..1fbd940408 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -366,7 +366,7 @@ test_expect_success 'Query master@{2005-05-25} (before history)' '
test_when_finished "rm -f o e" &&
git rev-parse --verify master@{2005-05-25} >o 2>e &&
test $C = $(cat o) &&
- echo test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+ test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
'
test_expect_success 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' '
test_when_finished "rm -f o e" &&
diff --git a/t/t1500-rev-parse.sh b/t/t1500-rev-parse.sh
index 5c715fe2cf..01abee533d 100755
--- a/t/t1500-rev-parse.sh
+++ b/t/t1500-rev-parse.sh
@@ -142,6 +142,22 @@ test_expect_success 'showing the superproject correctly' '
git -C super submodule add ../sub dir/sub &&
echo $(pwd)/super >expect &&
git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
+ test_cmp expect out &&
+
+ test_commit -C super submodule_add &&
+ git -C super checkout -b branch1 &&
+ git -C super/dir/sub checkout -b branch1 &&
+ test_commit -C super/dir/sub branch1_commit &&
+ git -C super add dir/sub &&
+ test_commit -C super branch1_commit &&
+ git -C super checkout -b branch2 master &&
+ git -C super/dir/sub checkout -b branch2 master &&
+ test_commit -C super/dir/sub branch2_commit &&
+ git -C super add dir/sub &&
+ test_commit -C super branch2_commit &&
+ test_must_fail git -C super merge branch1 &&
+
+ git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
test_cmp expect out
'
diff --git a/t/t1700-split-index.sh b/t/t1700-split-index.sh
index be22398a85..2ac47aa0e4 100755
--- a/t/t1700-split-index.sh
+++ b/t/t1700-split-index.sh
@@ -6,7 +6,19 @@ test_description='split index mode tests'
# We need total control of index splitting here
sane_unset GIT_TEST_SPLIT_INDEX
-sane_unset GIT_FSMONITOR_TEST
+
+# Testing a hard coded SHA against an index with an extension
+# that can vary from run to run is problematic so we disable
+# those extensions.
+sane_unset GIT_TEST_FSMONITOR
+sane_unset GIT_TEST_INDEX_THREADS
+
+# Create a file named as $1 with content read from stdin.
+# Set the file's mtime to a few seconds in the past to avoid racy situations.
+create_non_racy_file () {
+ cat >"$1" &&
+ test-tool chmtime =-5 "$1"
+}
test_expect_success 'enable split index' '
git config splitIndex.maxPercentChange 100 &&
@@ -15,11 +27,11 @@ test_expect_success 'enable split index' '
indexversion=$(test-tool index-version <.git/index) &&
if test "$indexversion" = "4"
then
- own=432ef4b63f32193984f339431fd50ca796493569
- base=508851a7f0dfa8691e9f69c7f055865389012491
+ own=3527df833c6c100d3d1d921a9a782d62a8be4b58
+ base=746f7ab2ed44fb839efdfbffcf399d0b113fb4cb
else
- own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339
- base=39d890139ee5356c7ef572216cebcd27aa41f9df
+ own=5e9b60117ece18da410ddecc8b8d43766a0e4204
+ base=4370042739b31cd17a5c5cd6043a77c9a00df113
fi &&
cat >expect <<-EOF &&
own $own
@@ -31,7 +43,7 @@ test_expect_success 'enable split index' '
'
test_expect_success 'add one file' '
- : >one &&
+ create_non_racy_file one &&
git update-index --add one &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
@@ -83,7 +95,7 @@ test_expect_success 'enable split index again, "one" now belongs to base index"'
'
test_expect_success 'modify original file, base index untouched' '
- echo modified >one &&
+ echo modified | create_non_racy_file one &&
git update-index one &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
@@ -102,7 +114,7 @@ test_expect_success 'modify original file, base index untouched' '
'
test_expect_success 'add another file, which stays index' '
- : >two &&
+ create_non_racy_file two &&
git update-index --add two &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
@@ -155,7 +167,7 @@ test_expect_success 'remove file in base index' '
'
test_expect_success 'add original file back' '
- : >one &&
+ create_non_racy_file one &&
git update-index --add one &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
@@ -174,7 +186,7 @@ test_expect_success 'add original file back' '
'
test_expect_success 'add new file' '
- : >two &&
+ create_non_racy_file two &&
git update-index --add two &&
git ls-files --stage >actual &&
cat >expect <<-EOF &&
@@ -218,7 +230,7 @@ test_expect_success 'rev-parse --shared-index-path' '
test_expect_success 'set core.splitIndex config variable to true' '
git config core.splitIndex true &&
- : >three &&
+ create_non_racy_file three &&
git update-index --add three &&
git ls-files --stage >ls-files.actual &&
cat >ls-files.expect <<-EOF &&
@@ -253,9 +265,9 @@ test_expect_success 'set core.splitIndex config variable to false' '
test_cmp expect actual
'
-test_expect_success 'set core.splitIndex config variable to true' '
+test_expect_success 'set core.splitIndex config variable back to true' '
git config core.splitIndex true &&
- : >three &&
+ create_non_racy_file three &&
git update-index --add three &&
BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -265,7 +277,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
deletions:
EOF
test_cmp expect actual &&
- : >four &&
+ create_non_racy_file four &&
git update-index --add four &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
cat >expect <<-EOF &&
@@ -279,7 +291,7 @@ test_expect_success 'set core.splitIndex config variable to true' '
test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
git config --unset splitIndex.maxPercentChange &&
- : >five &&
+ create_non_racy_file five &&
git update-index --add five &&
BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -289,7 +301,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
deletions:
EOF
test_cmp expect actual &&
- : >six &&
+ create_non_racy_file six &&
git update-index --add six &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
cat >expect <<-EOF &&
@@ -303,7 +315,7 @@ test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
test_expect_success 'check splitIndex.maxPercentChange set to 0' '
git config splitIndex.maxPercentChange 0 &&
- : >seven &&
+ create_non_racy_file seven &&
git update-index --add seven &&
BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -313,7 +325,7 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
deletions:
EOF
test_cmp expect actual &&
- : >eight &&
+ create_non_racy_file eight &&
git update-index --add eight &&
BASE=$(test-tool dump-split-index .git/index | grep "^base") &&
test-tool dump-split-index .git/index | sed "/^own/d" >actual &&
@@ -326,17 +338,17 @@ test_expect_success 'check splitIndex.maxPercentChange set to 0' '
'
test_expect_success 'shared index files expire after 2 weeks by default' '
- : >ten &&
+ create_non_racy_file ten &&
git update-index --add ten &&
test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
just_under_2_weeks_ago=$((5-14*86400)) &&
test-tool chmtime =$just_under_2_weeks_ago .git/sharedindex.* &&
- : >eleven &&
+ create_non_racy_file eleven &&
git update-index --add eleven &&
test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
just_over_2_weeks_ago=$((-1-14*86400)) &&
test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
- : >twelve &&
+ create_non_racy_file twelve &&
git update-index --add twelve &&
test $(ls .git/sharedindex.* | wc -l) -le 2
'
@@ -344,12 +356,12 @@ test_expect_success 'shared index files expire after 2 weeks by default' '
test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' '
git config splitIndex.sharedIndexExpire "16.days.ago" &&
test-tool chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
- : >thirteen &&
+ create_non_racy_file thirteen &&
git update-index --add thirteen &&
test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
just_over_16_days_ago=$((-1-16*86400)) &&
test-tool chmtime =$just_over_16_days_ago .git/sharedindex.* &&
- : >fourteen &&
+ create_non_racy_file fourteen &&
git update-index --add fourteen &&
test $(ls .git/sharedindex.* | wc -l) -le 2
'
@@ -358,13 +370,13 @@ test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"
git config splitIndex.sharedIndexExpire never &&
just_10_years_ago=$((-365*10*86400)) &&
test-tool chmtime =$just_10_years_ago .git/sharedindex.* &&
- : >fifteen &&
+ create_non_racy_file fifteen &&
git update-index --add fifteen &&
test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
git config splitIndex.sharedIndexExpire now &&
just_1_second_ago=-1 &&
test-tool chmtime =$just_1_second_ago .git/sharedindex.* &&
- : >sixteen &&
+ create_non_racy_file sixteen &&
git update-index --add sixteen &&
test $(ls .git/sharedindex.* | wc -l) -le 2
'
@@ -379,7 +391,7 @@ do
# Create one new shared index file
git config core.sharedrepository "$mode" &&
git config core.splitIndex true &&
- : >one &&
+ create_non_racy_file one &&
git update-index --add one &&
echo "$modebits" >expect &&
test_modebits .git/index >actual &&
diff --git a/t/t1701-racy-split-index.sh b/t/t1701-racy-split-index.sh
new file mode 100755
index 0000000000..5dc221ef38
--- /dev/null
+++ b/t/t1701-racy-split-index.sh
@@ -0,0 +1,214 @@
+#!/bin/sh
+
+# This test can give false success if your machine is sufficiently
+# slow or all trials happened to happen on second boundaries.
+
+test_description='racy split index'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ # Only split the index when the test explicitly says so.
+ sane_unset GIT_TEST_SPLIT_INDEX &&
+ git config splitIndex.maxPercentChange 100 &&
+
+ echo "cached content" >racy-file &&
+ git add racy-file &&
+ git commit -m initial &&
+
+ echo something >other-file &&
+ # No raciness with this file.
+ test-tool chmtime =-20 other-file &&
+
+ echo "+cached content" >expect
+'
+
+check_cached_diff () {
+ git diff-index --patch --cached $EMPTY_TREE racy-file >diff &&
+ tail -1 diff >actual &&
+ test_cmp expect actual
+}
+
+trials="0 1 2 3 4"
+for trial in $trials
+do
+ test_expect_success "split the index while adding a racily clean file #$trial" '
+ rm -f .git/index .git/sharedindex.* &&
+
+ # The next three commands must be run within the same
+ # second (so both writes to racy-file result in the same
+ # mtime) to create the interesting racy situation.
+ echo "cached content" >racy-file &&
+
+ # Update and split the index. The cache entry of
+ # racy-file will be stored only in the shared index.
+ git update-index --split-index --add racy-file &&
+
+ # File size must stay the same.
+ echo "dirty worktree" >racy-file &&
+
+ # Subsequent git commands should notice that racy-file
+ # and the split index have the same mtime, and check
+ # the content of the file to see if it is actually
+ # clean.
+ check_cached_diff
+ '
+done
+
+for trial in $trials
+do
+ test_expect_success "add a racily clean file to an already split index #$trial" '
+ rm -f .git/index .git/sharedindex.* &&
+
+ git update-index --split-index &&
+
+ # The next three commands must be run within the same
+ # second.
+ echo "cached content" >racy-file &&
+
+ # Update the split index. The cache entry of racy-file
+ # will be stored only in the split index.
+ git update-index --add racy-file &&
+
+ # File size must stay the same.
+ echo "dirty worktree" >racy-file &&
+
+ # Subsequent git commands should notice that racy-file
+ # and the split index have the same mtime, and check
+ # the content of the file to see if it is actually
+ # clean.
+ check_cached_diff
+ '
+done
+
+for trial in $trials
+do
+ test_expect_success "split the index when the index contains a racily clean cache entry #$trial" '
+ rm -f .git/index .git/sharedindex.* &&
+
+ # The next three commands must be run within the same
+ # second.
+ echo "cached content" >racy-file &&
+
+ git update-index --add racy-file &&
+
+ # File size must stay the same.
+ echo "dirty worktree" >racy-file &&
+
+ # Now wait a bit to ensure that the split index written
+ # below will get a more recent mtime than racy-file.
+ sleep 1 &&
+
+ # Update and split the index when the index contains
+ # the racily clean cache entry of racy-file.
+ # A corresponding replacement cache entry with smudged
+ # stat data should be added to the new split index.
+ git update-index --split-index --add other-file &&
+
+ # Subsequent git commands should notice the smudged
+ # stat data in the replacement cache entry and that it
+ # doesnt match with the file the worktree.
+ check_cached_diff
+ '
+done
+
+for trial in $trials
+do
+ test_expect_success "update the split index when it contains a new racily clean cache entry #$trial" '
+ rm -f .git/index .git/sharedindex.* &&
+
+ git update-index --split-index &&
+
+ # The next three commands must be run within the same
+ # second.
+ echo "cached content" >racy-file &&
+
+ # Update the split index. The cache entry of racy-file
+ # will be stored only in the split index.
+ git update-index --add racy-file &&
+
+ # File size must stay the same.
+ echo "dirty worktree" >racy-file &&
+
+ # Now wait a bit to ensure that the split index written
+ # below will get a more recent mtime than racy-file.
+ sleep 1 &&
+
+ # Update the split index when the racily clean cache
+ # entry of racy-file is only stored in the split index.
+ # An updated cache entry with smudged stat data should
+ # be added to the new split index.
+ git update-index --add other-file &&
+
+ # Subsequent git commands should notice the smudged
+ # stat data.
+ check_cached_diff
+ '
+done
+
+for trial in $trials
+do
+ test_expect_success "update the split index when a racily clean cache entry is stored only in the shared index #$trial" '
+ rm -f .git/index .git/sharedindex.* &&
+
+ # The next three commands must be run within the same
+ # second.
+ echo "cached content" >racy-file &&
+
+ # Update and split the index. The cache entry of
+ # racy-file will be stored only in the shared index.
+ git update-index --split-index --add racy-file &&
+
+ # File size must stay the same.
+ echo "dirty worktree" >racy-file &&
+
+ # Now wait a bit to ensure that the split index written
+ # below will get a more recent mtime than racy-file.
+ sleep 1 &&
+
+ # Update the split index when the racily clean cache
+ # entry of racy-file is only stored in the shared index.
+ # A corresponding replacement cache entry with smudged
+ # stat data should be added to the new split index.
+ git update-index --add other-file &&
+
+ # Subsequent git commands should notice the smudged
+ # stat data.
+ check_cached_diff
+ '
+done
+
+for trial in $trials
+do
+ test_expect_success "update the split index after unpack trees() copied a racily clean cache entry from the shared index #$trial" '
+ rm -f .git/index .git/sharedindex.* &&
+
+ # The next three commands must be run within the same
+ # second.
+ echo "cached content" >racy-file &&
+
+ # Update and split the index. The cache entry of
+ # racy-file will be stored only in the shared index.
+ git update-index --split-index --add racy-file &&
+
+ # File size must stay the same.
+ echo "dirty worktree" >racy-file &&
+
+ # Now wait a bit to ensure that the split index written
+ # below will get a more recent mtime than racy-file.
+ sleep 1 &&
+
+ # Update the split index after unpack_trees() copied the
+ # racily clean cache entry of racy-file from the shared
+ # index. A corresponding replacement cache entry
+ # with smudged stat data should be added to the new
+ # split index.
+ git read-tree -m HEAD &&
+
+ # Subsequent git commands should notice the smudged
+ # stat data.
+ check_cached_diff
+ '
+done
+
+test_done
diff --git a/t/t2101-update-index-reupdate.sh b/t/t2101-update-index-reupdate.sh
index 685ec45639..6c32d42c8c 100755
--- a/t/t2101-update-index-reupdate.sh
+++ b/t/t2101-update-index-reupdate.sh
@@ -73,7 +73,7 @@ test_expect_success 'update-index --update from subdir' '
100644 $(git hash-object dir1/file3) 0 dir1/file3
100644 $file2 0 file2
EOF
- test_cmp current expected
+ test_cmp expected current
'
test_expect_success 'update-index --update with pathspec' '
diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh
index 93f21ab078..478b82cf9b 100755
--- a/t/t3200-branch.sh
+++ b/t/t3200-branch.sh
@@ -1221,7 +1221,7 @@ test_expect_success 'use --edit-description' '
EOF
EDITOR=./editor git branch --edit-description &&
echo "New contents" >expect &&
- test_cmp EDITOR_OUTPUT expect
+ test_cmp expect EDITOR_OUTPUT
'
test_expect_success 'detect typo in branch name when using --edit-description' '
diff --git a/t/t3320-notes-merge-worktrees.sh b/t/t3320-notes-merge-worktrees.sh
index 10bfc8b947..823fdbda1f 100755
--- a/t/t3320-notes-merge-worktrees.sh
+++ b/t/t3320-notes-merge-worktrees.sh
@@ -44,7 +44,7 @@ test_expect_success 'merge z into y fails and sets NOTES_MERGE_REF' '
git config core.notesRef refs/notes/y &&
test_must_fail git notes merge z &&
echo "ref: refs/notes/y" >expect &&
- test_cmp .git/NOTES_MERGE_REF expect
+ test_cmp expect .git/NOTES_MERGE_REF
'
test_expect_success 'merge z into y while mid-merge in another workdir fails' '
@@ -66,7 +66,7 @@ test_expect_success 'merge z into x while mid-merge on y succeeds' '
grep -v "A notes merge into refs/notes/x is already in-progress in" out
) &&
echo "ref: refs/notes/x" >expect &&
- test_cmp .git/worktrees/worktree2/NOTES_MERGE_REF expect
+ test_cmp expect .git/worktrees/worktree2/NOTES_MERGE_REF
'
test_done
diff --git a/t/t3400-rebase.sh b/t/t3400-rebase.sh
index 3996ee0135..3e73f7584c 100755
--- a/t/t3400-rebase.sh
+++ b/t/t3400-rebase.sh
@@ -183,13 +183,13 @@ test_expect_success 'cherry-picked commits and fork-point work together' '
test_commit final_B B "Final B" &&
git rebase &&
echo Amended >expect &&
- test_cmp A expect &&
+ test_cmp expect A &&
echo "Final B" >expect &&
- test_cmp B expect &&
+ test_cmp expect B &&
echo C >expect &&
- test_cmp C expect &&
+ test_cmp expect C &&
echo D >expect &&
- test_cmp D expect
+ test_cmp expect D
'
test_expect_success 'rebase -q is quiet' '
diff --git a/t/t3417-rebase-whitespace-fix.sh b/t/t3417-rebase-whitespace-fix.sh
index 1fb3e499b4..e85cdc7037 100755
--- a/t/t3417-rebase-whitespace-fix.sh
+++ b/t/t3417-rebase-whitespace-fix.sh
@@ -55,7 +55,7 @@ test_expect_success 'blank line at end of file; extend at end of file' '
git add file && git commit -m second &&
git rebase --whitespace=fix HEAD^^ &&
git diff --exit-code HEAD^:file expect-first &&
- test_cmp file expect-second
+ test_cmp expect-second file
'
# prepare third revision of "file"
@@ -82,7 +82,7 @@ test_expect_success 'two blanks line at end of file; extend at end of file' '
cp third file && git add file && git commit -m third &&
git rebase --whitespace=fix HEAD^^ &&
git diff --exit-code HEAD^:file expect-second &&
- test_cmp file expect-third
+ test_cmp expect-third file
'
test_expect_success 'same, but do not remove trailing spaces' '
@@ -120,7 +120,7 @@ test_expect_success 'at beginning of file' '
done >> file &&
git commit -m more file &&
git rebase --whitespace=fix HEAD^^ &&
- test_cmp file expect-beginning
+ test_cmp expect-beginning file
'
test_done
diff --git a/t/t3702-add-edit.sh b/t/t3702-add-edit.sh
index c6af7f82b5..6c676645d8 100755
--- a/t/t3702-add-edit.sh
+++ b/t/t3702-add-edit.sh
@@ -110,10 +110,10 @@ test_expect_success 'add -e' '
cp second-part file &&
git add -e &&
test_cmp second-part file &&
- test_cmp orig-patch expected-patch &&
+ test_cmp expected-patch orig-patch &&
git diff --cached >actual &&
grep -v index actual >out &&
- test_cmp out expected
+ test_cmp expected out
'
diff --git a/t/t3903-stash.sh b/t/t3903-stash.sh
index 6450bc6698..cd216655b9 100755
--- a/t/t3903-stash.sh
+++ b/t/t3903-stash.sh
@@ -36,7 +36,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 output expect
+ test_cmp expect output
'
test_expect_success 'applying bogus stash does nothing' '
@@ -210,9 +210,9 @@ 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 output expect &&
+ test_cmp expect output &&
git diff > output &&
- test_cmp output expect1 &&
+ test_cmp expect1 output &&
git add file &&
git commit -m alternate\ second &&
git diff master..stashbranch > output &&
@@ -710,7 +710,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 output expect
+ test_cmp expect output
'
test_expect_success 'store called with invalid commit' '
diff --git a/t/t3905-stash-include-untracked.sh b/t/t3905-stash-include-untracked.sh
index 597b0637d1..cc1c8a7bb2 100755
--- a/t/t3905-stash-include-untracked.sh
+++ b/t/t3905-stash-include-untracked.sh
@@ -142,7 +142,7 @@ test_expect_success 'stash save --include-untracked removed files' '
rm -f file &&
git stash save --include-untracked &&
echo 1 > expect &&
- test_cmp file expect
+ test_cmp expect file
'
rm -f expect
diff --git a/t/t4025-hunk-header.sh b/t/t4025-hunk-header.sh
index fa44e78869..35578f2bb9 100755
--- a/t/t4025-hunk-header.sh
+++ b/t/t4025-hunk-header.sh
@@ -37,7 +37,7 @@ test_expect_success 'hunk header truncation with an overly long line' '
echo " A $N$N$N$N$N$N$N$N$N2" &&
echo " L $N$N$N$N$N$N$N$N$N1"
) >expected &&
- test_cmp actual expected
+ test_cmp expected actual
'
diff --git a/t/t4117-apply-reject.sh b/t/t4117-apply-reject.sh
index d80187de94..f7de6f077a 100755
--- a/t/t4117-apply-reject.sh
+++ b/t/t4117-apply-reject.sh
@@ -72,7 +72,7 @@ test_expect_success 'apply with --reject should fail but update the file' '
rm -f file1.rej file2.rej &&
test_must_fail git apply --reject patch.1 &&
- test_cmp file1 expected &&
+ test_cmp expected file1 &&
cat file1.rej &&
test_path_is_missing file2.rej
@@ -85,7 +85,7 @@ test_expect_success 'apply with --reject should fail but update the file' '
test_must_fail git apply --reject patch.2 >rejects &&
test_path_is_missing file1 &&
- test_cmp file2 expected &&
+ test_cmp expected file2 &&
cat file2.rej &&
test_path_is_missing file1.rej
@@ -99,7 +99,7 @@ test_expect_success 'the same test with --verbose' '
test_must_fail git apply --reject --verbose patch.2 >rejects &&
test_path_is_missing file1 &&
- test_cmp file2 expected &&
+ test_cmp expected file2 &&
cat file2.rej &&
test_path_is_missing file1.rej
diff --git a/t/t4124-apply-ws-rule.sh b/t/t4124-apply-ws-rule.sh
index 7e32237a2a..ff51e9e789 100755
--- a/t/t4124-apply-ws-rule.sh
+++ b/t/t4124-apply-ws-rule.sh
@@ -313,9 +313,9 @@ test_expect_success 'applying beyond EOF requires one non-blank context line' '
{ echo a; echo; } >one &&
cp one expect &&
test_must_fail git apply --whitespace=fix patch &&
- test_cmp one expect &&
+ test_cmp expect one &&
test_must_fail git apply --ignore-space-change --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'tons of blanks at EOF should not apply' '
@@ -342,10 +342,10 @@ test_expect_success 'missing blank line at end with --whitespace=fix' '
cp one saved-one &&
test_must_fail git apply patch &&
git apply --whitespace=fix patch &&
- test_cmp one expect &&
+ test_cmp expect one &&
mv saved-one one &&
git apply --ignore-space-change --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'two missing blank lines at end with --whitespace=fix' '
@@ -360,11 +360,11 @@ test_expect_success 'two missing blank lines at end with --whitespace=fix' '
cp no-blank-lines one &&
test_must_fail git apply patch &&
git apply --whitespace=fix patch &&
- test_cmp one expect &&
+ test_cmp expect one &&
mv no-blank-lines one &&
test_must_fail git apply patch &&
git apply --ignore-space-change --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'missing blank line at end, insert before end, --whitespace=fix' '
@@ -376,7 +376,7 @@ test_expect_success 'missing blank line at end, insert before end, --whitespace=
echo a >one &&
test_must_fail git apply patch &&
git apply --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'shrink file with tons of missing blanks at end of file' '
@@ -392,10 +392,10 @@ test_expect_success 'shrink file with tons of missing blanks at end of file' '
cp no-blank-lines one &&
test_must_fail git apply patch &&
git apply --whitespace=fix patch &&
- test_cmp one expect &&
+ test_cmp expect one &&
mv no-blank-lines one &&
git apply --ignore-space-change --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'missing blanks at EOF must only match blank lines' '
@@ -427,7 +427,7 @@ test_expect_success 'missing blank line should match context line with spaces' '
git add one &&
git apply --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
sed -e's/Z//' >one <<EOF
@@ -447,7 +447,7 @@ test_expect_success 'same, but with the --ignore-space-option' '
git checkout-index -f one &&
git apply --ignore-space-change --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
@@ -464,7 +464,7 @@ test_expect_success 'same, but with CR-LF line endings && cr-at-eol set' '
mv save-one one &&
git apply --ignore-space-change --whitespace=fix patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'CR-LF line endings && add line && text=auto' '
@@ -478,7 +478,7 @@ test_expect_success 'CR-LF line endings && add line && text=auto' '
mv save-one one &&
echo "one text=auto" >.gitattributes &&
git apply patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'CR-LF line endings && change line && text=auto' '
@@ -491,7 +491,7 @@ test_expect_success 'CR-LF line endings && change line && text=auto' '
mv save-one one &&
echo "one text=auto" >.gitattributes &&
git apply patch &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'LF in repo, CRLF in worktree && change line && text=auto' '
@@ -503,7 +503,7 @@ test_expect_success 'LF in repo, CRLF in worktree && change line && text=auto' '
echo "one text=auto" >.gitattributes &&
git -c core.eol=CRLF apply patch &&
printf "b\r\n" >expect &&
- test_cmp one expect
+ test_cmp expect one
'
test_expect_success 'whitespace=fix to expand' '
diff --git a/t/t4138-apply-ws-expansion.sh b/t/t4138-apply-ws-expansion.sh
index 0ffe33fbef..3b636a63a3 100755
--- a/t/t4138-apply-ws-expansion.sh
+++ b/t/t4138-apply-ws-expansion.sh
@@ -114,7 +114,7 @@ for t in 1 2 3 4
do
test_expect_success 'apply with ws expansion (t=$t)' '
git apply patch$t.patch &&
- test_cmp test-$t expect-$t
+ test_cmp expect-$t test-$t
'
done
diff --git a/t/t4214-log-graph-octopus.sh b/t/t4214-log-graph-octopus.sh
new file mode 100755
index 0000000000..dab96c89aa
--- /dev/null
+++ b/t/t4214-log-graph-octopus.sh
@@ -0,0 +1,102 @@
+#!/bin/sh
+
+test_description='git log --graph of skewed left octopus merge.'
+
+. ./test-lib.sh
+
+test_expect_success 'set up merge history' '
+ cat >expect.uncolored <<-\EOF &&
+ * left
+ | *---. octopus-merge
+ | |\ \ \
+ |/ / / /
+ | | | * 4
+ | | * | 3
+ | | |/
+ | * | 2
+ | |/
+ * | 1
+ |/
+ * initial
+ EOF
+ cat >expect.colors <<-\EOF &&
+ * left
+ <RED>|<RESET> *<BLUE>-<RESET><BLUE>-<RESET><MAGENTA>-<RESET><MAGENTA>.<RESET> octopus-merge
+ <RED>|<RESET> <RED>|<RESET><YELLOW>\<RESET> <BLUE>\<RESET> <MAGENTA>\<RESET>
+ <RED>|<RESET><RED>/<RESET> <YELLOW>/<RESET> <BLUE>/<RESET> <MAGENTA>/<RESET>
+ <RED>|<RESET> <YELLOW>|<RESET> <BLUE>|<RESET> * 4
+ <RED>|<RESET> <YELLOW>|<RESET> * <MAGENTA>|<RESET> 3
+ <RED>|<RESET> <YELLOW>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+ <RED>|<RESET> * <MAGENTA>|<RESET> 2
+ <RED>|<RESET> <MAGENTA>|<RESET><MAGENTA>/<RESET>
+ * <MAGENTA>|<RESET> 1
+ <MAGENTA>|<RESET><MAGENTA>/<RESET>
+ * initial
+ EOF
+ test_commit initial &&
+ for i in 1 2 3 4 ; do
+ git checkout master -b $i || return $?
+ # Make tag name different from branch name, to avoid
+ # ambiguity error when calling checkout.
+ test_commit $i $i $i tag$i || return $?
+ done &&
+ git checkout 1 -b merge &&
+ test_tick &&
+ git merge -m octopus-merge 1 2 3 4 &&
+ git checkout 1 -b L &&
+ test_commit left
+'
+
+test_expect_success 'log --graph with tricky octopus merge with colors' '
+ test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+ git log --color=always --graph --date-order --pretty=tformat:%s --all >actual.colors.raw &&
+ test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+ test_cmp expect.colors actual.colors
+'
+
+test_expect_success 'log --graph with tricky octopus merge, no color' '
+ git log --color=never --graph --date-order --pretty=tformat:%s --all >actual.raw &&
+ sed "s/ *\$//" actual.raw >actual &&
+ test_cmp expect.uncolored actual
+'
+
+# Repeat the previous two tests with "normal" octopus merge (i.e.,
+# without the first parent skewing to the "left" branch column).
+
+test_expect_success 'log --graph with normal octopus merge, no color' '
+ cat >expect.uncolored <<-\EOF &&
+ *---. octopus-merge
+ |\ \ \
+ | | | * 4
+ | | * | 3
+ | | |/
+ | * | 2
+ | |/
+ * | 1
+ |/
+ * initial
+ EOF
+ git log --color=never --graph --date-order --pretty=tformat:%s merge >actual.raw &&
+ sed "s/ *\$//" actual.raw >actual &&
+ test_cmp expect.uncolored actual
+'
+
+test_expect_success 'log --graph with normal octopus merge with colors' '
+ cat >expect.colors <<-\EOF &&
+ *<YELLOW>-<RESET><YELLOW>-<RESET><BLUE>-<RESET><BLUE>.<RESET> octopus-merge
+ <RED>|<RESET><GREEN>\<RESET> <YELLOW>\<RESET> <BLUE>\<RESET>
+ <RED>|<RESET> <GREEN>|<RESET> <YELLOW>|<RESET> * 4
+ <RED>|<RESET> <GREEN>|<RESET> * <BLUE>|<RESET> 3
+ <RED>|<RESET> <GREEN>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+ <RED>|<RESET> * <BLUE>|<RESET> 2
+ <RED>|<RESET> <BLUE>|<RESET><BLUE>/<RESET>
+ * <BLUE>|<RESET> 1
+ <BLUE>|<RESET><BLUE>/<RESET>
+ * initial
+ EOF
+ test_config log.graphColors red,green,yellow,blue,magenta,cyan &&
+ git log --color=always --graph --date-order --pretty=tformat:%s merge >actual.colors.raw &&
+ test_decode_color <actual.colors.raw | sed "s/ *\$//" >actual.colors &&
+ test_cmp expect.colors actual.colors
+'
+test_done
diff --git a/t/t5317-pack-objects-filter-objects.sh b/t/t5317-pack-objects-filter-objects.sh
index 6710c8bc8c..2e718f0bde 100755
--- a/t/t5317-pack-objects-filter-objects.sh
+++ b/t/t5317-pack-objects-filter-objects.sh
@@ -21,17 +21,21 @@ test_expect_success 'setup r1' '
test_expect_success 'verify blob count in normal packfile' '
git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
- | awk -f print_2.awk \
- | sort >expected &&
+ >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r1 pack-objects --rev --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r1 index-pack ../all.pack &&
- git -C r1 verify-pack -v ../all.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r1 verify-pack -v ../all.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:none packfile has no blobs' '
@@ -39,24 +43,28 @@ test_expect_success 'verify blob:none packfile has no blobs' '
HEAD
EOF
git -C r1 index-pack ../filter.pack &&
- git -C r1 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
+
+ git -C r1 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
nr=$(wc -l <observed) &&
test 0 -eq $nr
'
test_expect_success 'verify normal and blob:none packfiles have same commits/trees' '
- git -C r1 verify-pack -v ../all.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >expected &&
- git -C r1 verify-pack -v ../filter.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+ git -C r1 verify-pack -v ../all.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >expected &&
+
+ git -C r1 verify-pack -v ../filter.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
# Test blob:limit=<n>[kmg] filter.
@@ -75,18 +83,21 @@ test_expect_success 'setup r2' '
'
test_expect_success 'verify blob count in normal packfile' '
- git -C r2 ls-files -s large.1000 large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r2 pack-objects --rev --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../all.pack &&
- git -C r2 verify-pack -v ../all.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r2 verify-pack -v ../all.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=500 omits all blobs' '
@@ -94,10 +105,12 @@ test_expect_success 'verify blob:limit=500 omits all blobs' '
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
nr=$(wc -l <observed) &&
test 0 -eq $nr
'
@@ -107,100 +120,119 @@ test_expect_success 'verify blob:limit=1000' '
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
nr=$(wc -l <observed) &&
test 0 -eq $nr
'
test_expect_success 'verify blob:limit=1001' '
- git -C r2 ls-files -s large.1000 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r2 pack-objects --rev --stdout --filter=blob:limit=1001 >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=10001' '
- git -C r2 ls-files -s large.1000 large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r2 pack-objects --rev --stdout --filter=blob:limit=10001 >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=1k' '
- git -C r2 ls-files -s large.1000 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r2 ls-files -s large.1000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify explicitly specifying oversized blob in input' '
- git -C r2 ls-files -s large.1000 large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r2 pack-objects --rev --stdout --filter=blob:limit=1k >filter.pack <<-EOF &&
HEAD
$(git -C r2 rev-parse HEAD:large.10000)
EOF
git -C r2 index-pack ../filter.pack &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=1m' '
- git -C r2 ls-files -s large.1000 large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r2 pack-objects --rev --stdout --filter=blob:limit=1m >filter.pack <<-EOF &&
HEAD
EOF
git -C r2 index-pack ../filter.pack &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify normal and blob:limit packfiles have same commits/trees' '
- git -C r2 verify-pack -v ../all.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >expected &&
- git -C r2 verify-pack -v ../filter.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+ git -C r2 verify-pack -v ../all.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >expected &&
+
+ git -C r2 verify-pack -v ../filter.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
# Test sparse:path=<path> filter.
@@ -225,71 +257,85 @@ test_expect_success 'setup r3' '
test_expect_success 'verify blob count in normal packfile' '
git -C r3 ls-files -s sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
+ >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r3 pack-objects --rev --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r3 index-pack ../all.pack &&
- git -C r3 verify-pack -v ../all.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r3 verify-pack -v ../all.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify sparse:path=pattern1' '
- git -C r3 ls-files -s dir1/sparse1 dir1/sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r3 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern1 >filter.pack <<-EOF &&
HEAD
EOF
git -C r3 index-pack ../filter.pack &&
- git -C r3 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r3 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify normal and sparse:path=pattern1 packfiles have same commits/trees' '
- git -C r3 verify-pack -v ../all.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >expected &&
- git -C r3 verify-pack -v ../filter.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+ git -C r3 verify-pack -v ../all.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >expected &&
+
+ git -C r3 verify-pack -v ../filter.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify sparse:path=pattern2' '
- git -C r3 ls-files -s sparse1 dir1/sparse1 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r3 ls-files -s sparse1 dir1/sparse1 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r3 pack-objects --rev --stdout --filter=sparse:path=../pattern2 >filter.pack <<-EOF &&
HEAD
EOF
git -C r3 index-pack ../filter.pack &&
- git -C r3 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r3 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify normal and sparse:path=pattern2 packfiles have same commits/trees' '
- git -C r3 verify-pack -v ../all.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >expected &&
- git -C r3 verify-pack -v ../filter.pack \
- | grep -E "commit|tree" \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+ git -C r3 verify-pack -v ../all.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >expected &&
+
+ git -C r3 verify-pack -v ../filter.pack >verify_result &&
+ grep -E "commit|tree" verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
# Test sparse:oid=<oid-ish> filter.
@@ -313,48 +359,58 @@ test_expect_success 'setup r4' '
test_expect_success 'verify blob count in normal packfile' '
git -C r4 ls-files -s pattern sparse1 sparse2 dir1/sparse1 dir1/sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
+ >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r4 pack-objects --rev --stdout >all.pack <<-EOF &&
HEAD
EOF
git -C r4 index-pack ../all.pack &&
- git -C r4 verify-pack -v ../all.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r4 verify-pack -v ../all.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify sparse:oid=OID' '
- git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
oid=$(git -C r4 ls-files -s pattern | awk -f print_2.awk) &&
git -C r4 pack-objects --rev --stdout --filter=sparse:oid=$oid >filter.pack <<-EOF &&
HEAD
EOF
git -C r4 index-pack ../filter.pack &&
- git -C r4 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r4 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify sparse:oid=oid-ish' '
- git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r4 ls-files -s dir1/sparse1 dir1/sparse2 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
git -C r4 pack-objects --rev --stdout --filter=sparse:oid=master:pattern >filter.pack <<-EOF &&
HEAD
EOF
git -C r4 index-pack ../filter.pack &&
- git -C r4 verify-pack -v ../filter.pack \
- | grep blob \
- | awk -f print_1.awk \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r4 verify-pack -v ../filter.pack >verify_result &&
+ grep blob verify_result |
+ awk -f print_1.awk |
+ sort >observed &&
+
+ test_cmp expected observed
'
# Delete some loose objects and use pack-objects, but WITHOUT any filtering.
@@ -362,8 +418,10 @@ test_expect_success 'verify sparse:oid=oid-ish' '
test_expect_success 'setup r1 - delete loose blobs' '
git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
- | awk -f print_2.awk \
- | sort >expected &&
+ >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
for id in `cat expected | sed "s|..|&/|"`
do
rm r1/.git/objects/$id
diff --git a/t/t5318-commit-graph.sh b/t/t5318-commit-graph.sh
index 75fe09521f..5fe21db99f 100755
--- a/t/t5318-commit-graph.sh
+++ b/t/t5318-commit-graph.sh
@@ -36,7 +36,7 @@ test_expect_success 'create commits and repack' '
graph_git_two_modes() {
git -c core.commitGraph=true $1 >output
git -c core.commitGraph=false $1 >expect
- test_cmp output expect
+ test_cmp expect output
}
graph_git_behavior() {
@@ -260,6 +260,66 @@ test_expect_success 'check that gc computes commit-graph' '
test_cmp_bin commit-graph-after-gc $objdir/info/commit-graph
'
+test_expect_success 'replace-objects invalidates commit-graph' '
+ cd "$TRASH_DIRECTORY" &&
+ test_when_finished rm -rf replace &&
+ git clone full replace &&
+ (
+ cd replace &&
+ git commit-graph write --reachable &&
+ test_path_is_file .git/objects/info/commit-graph &&
+ git replace HEAD~1 HEAD~2 &&
+ git -c core.commitGraph=false log >expect &&
+ git -c core.commitGraph=true log >actual &&
+ test_cmp expect actual &&
+ git commit-graph write --reachable &&
+ git -c core.commitGraph=false --no-replace-objects log >expect &&
+ git -c core.commitGraph=true --no-replace-objects log >actual &&
+ test_cmp expect actual &&
+ rm -rf .git/objects/info/commit-graph &&
+ git commit-graph write --reachable &&
+ test_path_is_file .git/objects/info/commit-graph
+ )
+'
+
+test_expect_success 'commit grafts invalidate commit-graph' '
+ cd "$TRASH_DIRECTORY" &&
+ test_when_finished rm -rf graft &&
+ git clone full graft &&
+ (
+ cd graft &&
+ git commit-graph write --reachable &&
+ test_path_is_file .git/objects/info/commit-graph &&
+ H1=$(git rev-parse --verify HEAD~1) &&
+ H3=$(git rev-parse --verify HEAD~3) &&
+ echo "$H1 $H3" >.git/info/grafts &&
+ git -c core.commitGraph=false log >expect &&
+ git -c core.commitGraph=true log >actual &&
+ test_cmp expect actual &&
+ git commit-graph write --reachable &&
+ git -c core.commitGraph=false --no-replace-objects log >expect &&
+ git -c core.commitGraph=true --no-replace-objects log >actual &&
+ test_cmp expect actual &&
+ rm -rf .git/objects/info/commit-graph &&
+ git commit-graph write --reachable &&
+ test_path_is_missing .git/objects/info/commit-graph
+ )
+'
+
+test_expect_success 'replace-objects invalidates commit-graph' '
+ cd "$TRASH_DIRECTORY" &&
+ test_when_finished rm -rf shallow &&
+ git clone --depth 2 "file://$TRASH_DIRECTORY/full" shallow &&
+ (
+ cd shallow &&
+ git commit-graph write --reachable &&
+ test_path_is_missing .git/objects/info/commit-graph &&
+ git fetch origin --unshallow &&
+ git commit-graph write --reachable &&
+ test_path_is_file .git/objects/info/commit-graph
+ )
+'
+
# the verify tests below expect the commit-graph to contain
# exactly the commits reachable from the commits/8 branch.
# If the file changes the set of commits in the list, then the
diff --git a/t/t5410-receive-pack-alternates.sh b/t/t5410-receive-pack-alternates.sh
new file mode 100755
index 0000000000..457c20c2a5
--- /dev/null
+++ b/t/t5410-receive-pack-alternates.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description='git receive-pack with alternate ref filtering'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+ test_commit base &&
+ git clone -s --bare . fork &&
+ git checkout -b public/branch master &&
+ test_commit public &&
+ git checkout -b private/branch master &&
+ test_commit private
+'
+
+extract_haves () {
+ depacketize | perl -lne '/^(\S+) \.have/ and print $1'
+}
+
+test_expect_success 'with core.alternateRefsCommand' '
+ write_script fork/alternate-refs <<-\EOF &&
+ git --git-dir="$1" for-each-ref \
+ --format="%(objectname)" \
+ refs/heads/public/
+ EOF
+ test_config -C fork core.alternateRefsCommand alternate-refs &&
+ git rev-parse public/branch >expect &&
+ printf "0000" | git receive-pack fork >actual &&
+ extract_haves <actual >actual.haves &&
+ test_cmp expect actual.haves
+'
+
+test_expect_success 'with core.alternateRefsPrefixes' '
+ test_config -C fork core.alternateRefsPrefixes "refs/heads/private" &&
+ git rev-parse private/branch >expect &&
+ printf "0000" | git receive-pack fork >actual &&
+ extract_haves <actual >actual.haves &&
+ test_cmp expect actual.haves
+'
+
+test_done
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index 1b5a4a6d38..086f2c40f6 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -50,8 +50,11 @@ pull_to_client () {
case "$heads" in *B*)
git update-ref refs/heads/B "$BTIP";;
esac &&
- git symbolic-ref HEAD refs/heads/$(echo $heads \
- | sed -e "s/^\(.\).*$/\1/") &&
+
+ git symbolic-ref HEAD refs/heads/$(
+ echo $heads |
+ sed -e "s/^\(.\).*$/\1/"
+ ) &&
git fsck --full &&
diff --git a/t/t5551-http-fetch-smart.sh b/t/t5551-http-fetch-smart.sh
index 771f36f9ff..8630b0cc39 100755
--- a/t/t5551-http-fetch-smart.sh
+++ b/t/t5551-http-fetch-smart.sh
@@ -23,26 +23,26 @@ test_expect_success 'create http-accessible bare repository' '
setup_askpass_helper
-cat >exp <<EOF
-> GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
-> Accept: */*
-> Accept-Encoding: ENCODINGS
-> Pragma: no-cache
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-advertisement
-> POST /smart/repo.git/git-upload-pack HTTP/1.1
-> Accept-Encoding: ENCODINGS
-> Content-Type: application/x-git-upload-pack-request
-> Accept: application/x-git-upload-pack-result
-> Content-Length: xxx
-< HTTP/1.1 200 OK
-< Pragma: no-cache
-< Cache-Control: no-cache, max-age=0, must-revalidate
-< Content-Type: application/x-git-upload-pack-result
-EOF
test_expect_success 'clone http repository' '
+ cat >exp <<-\EOF &&
+ > GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1
+ > Accept: */*
+ > Accept-Encoding: ENCODINGS
+ > Pragma: no-cache
+ < HTTP/1.1 200 OK
+ < Pragma: no-cache
+ < Cache-Control: no-cache, max-age=0, must-revalidate
+ < Content-Type: application/x-git-upload-pack-advertisement
+ > POST /smart/repo.git/git-upload-pack HTTP/1.1
+ > Accept-Encoding: ENCODINGS
+ > Content-Type: application/x-git-upload-pack-request
+ > Accept: application/x-git-upload-pack-result
+ > Content-Length: xxx
+ < HTTP/1.1 200 OK
+ < Pragma: no-cache
+ < Cache-Control: no-cache, max-age=0, must-revalidate
+ < Content-Type: application/x-git-upload-pack-result
+ EOF
GIT_TRACE_CURL=true git clone --quiet $HTTPD_URL/smart/repo.git clone 2>err &&
test_cmp file clone/file &&
tr '\''\015'\'' Q <err |
@@ -96,13 +96,13 @@ test_expect_success 'fetch changes via http' '
test_cmp file clone/file
'
-cat >exp <<EOF
-GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
-POST /smart/repo.git/git-upload-pack HTTP/1.1 200
-EOF
test_expect_success 'used upload-pack service' '
+ cat >exp <<-\EOF &&
+ GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+ POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+ GET /smart/repo.git/info/refs?service=git-upload-pack HTTP/1.1 200
+ POST /smart/repo.git/git-upload-pack HTTP/1.1 200
+ EOF
check_access_log exp
'
@@ -203,19 +203,19 @@ test_expect_success 'dumb clone via http-backend respects namespace' '
test_cmp expect actual
'
-cat >cookies.txt <<EOF
-127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
-EOF
-cat >expect_cookies.txt <<EOF
-
-127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
-127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value
-EOF
test_expect_success 'cookies stored in http.cookiefile when http.savecookies set' '
+ cat >cookies.txt <<-\EOF &&
+ 127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
+ EOF
+ sort >expect_cookies.txt <<-\EOF &&
+
+ 127.0.0.1 FALSE /smart_cookies/ FALSE 0 othername othervalue
+ 127.0.0.1 FALSE /smart_cookies/repo.git/info/ FALSE 0 name value
+ EOF
git config http.cookiefile cookies.txt &&
git config http.savecookies true &&
git ls-remote $HTTPD_URL/smart_cookies/repo.git master &&
- tail -3 cookies.txt >cookies_tail.txt &&
+ tail -3 cookies.txt | sort >cookies_tail.txt &&
test_cmp expect_cookies.txt cookies_tail.txt
'
@@ -381,6 +381,21 @@ test_expect_success 'using fetch command in remote-curl updates refs' '
test_cmp expect actual
'
+test_expect_success 'fetch by SHA-1 without tag following' '
+ SERVER="$HTTPD_DOCUMENT_ROOT_PATH/server" &&
+ rm -rf "$SERVER" client &&
+
+ git init "$SERVER" &&
+ test_commit -C "$SERVER" foo &&
+
+ git clone $HTTPD_URL/smart/server client &&
+
+ test_commit -C "$SERVER" bar &&
+ git -C "$SERVER" rev-parse bar >bar_hash &&
+ git -C client -c protocol.version=0 fetch \
+ --no-tags origin $(cat bar_hash)
+'
+
test_expect_success 'GIT_REDACT_COOKIES redacts cookies' '
rm -rf clone &&
echo "Set-Cookie: Foo=1" >cookies &&
diff --git a/t/t5616-partial-clone.sh b/t/t5616-partial-clone.sh
index bbbe7537df..6391437529 100755
--- a/t/t5616-partial-clone.sh
+++ b/t/t5616-partial-clone.sh
@@ -34,10 +34,12 @@ test_expect_success 'setup bare clone for server' '
# confirm partial clone was registered in the local config.
test_expect_success 'do partial clone 1' '
git clone --no-checkout --filter=blob:none "file://$(pwd)/srv.bare" pc1 &&
- git -C pc1 rev-list HEAD --quiet --objects --missing=print \
- | awk -f print_1.awk \
- | sed "s/?//" \
- | sort >observed.oids &&
+
+ git -C pc1 rev-list --quiet --objects --missing=print HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/?//" |
+ sort >observed.oids &&
+
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" &&
@@ -46,10 +48,10 @@ test_expect_success 'do partial clone 1' '
# checkout master to force dynamic object fetch of blobs at HEAD.
test_expect_success 'verify checkout with dynamic object fetch' '
- git -C pc1 rev-list HEAD --quiet --objects --missing=print >observed &&
+ git -C pc1 rev-list --quiet --objects --missing=print HEAD >observed &&
test_line_count = 4 observed &&
git -C pc1 checkout master &&
- git -C pc1 rev-list HEAD --quiet --objects --missing=print >observed &&
+ git -C pc1 rev-list --quiet --objects --missing=print HEAD >observed &&
test_line_count = 0 observed
'
@@ -72,7 +74,8 @@ test_expect_success 'push new commits to server' '
# have the new blobs.
test_expect_success 'partial fetch inherits filter settings' '
git -C pc1 fetch origin &&
- git -C pc1 rev-list master..origin/master --quiet --objects --missing=print >observed &&
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ master..origin/master >observed &&
test_line_count = 5 observed
'
@@ -80,7 +83,8 @@ test_expect_success 'partial fetch inherits filter settings' '
# we should only get 1 new blob (for the file in origin/master).
test_expect_success 'verify diff causes dynamic object fetch' '
git -C pc1 diff master..origin/master -- file.1.txt &&
- git -C pc1 rev-list master..origin/master --quiet --objects --missing=print >observed &&
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ master..origin/master >observed &&
test_line_count = 4 observed
'
@@ -89,7 +93,8 @@ test_expect_success 'verify diff causes dynamic object fetch' '
test_expect_success 'verify blame causes dynamic object fetch' '
git -C pc1 blame origin/master -- file.1.txt >observed.blame &&
test_cmp expect.blame observed.blame &&
- git -C pc1 rev-list master..origin/master --quiet --objects --missing=print >observed &&
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ master..origin/master >observed &&
test_line_count = 0 observed
'
@@ -109,7 +114,8 @@ test_expect_success 'push new commits to server for file.2.txt' '
# Verify we have all the new blobs.
test_expect_success 'override inherited filter-spec using --no-filter' '
git -C pc1 fetch --no-filter origin &&
- git -C pc1 rev-list master..origin/master --quiet --objects --missing=print >observed &&
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ master..origin/master >observed &&
test_line_count = 0 observed
'
@@ -130,16 +136,22 @@ test_expect_success 'push new commits to server for file.3.txt' '
# perhaps combined with a command in dry-run mode.
test_expect_success 'manual prefetch of missing objects' '
git -C pc1 fetch --filter=blob:none origin &&
- git -C pc1 rev-list master..origin/master --quiet --objects --missing=print \
- | awk -f print_1.awk \
- | sed "s/?//" \
- | sort >observed.oids &&
+
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ master..origin/master >revs &&
+ awk -f print_1.awk revs |
+ sed "s/?//" |
+ sort >observed.oids &&
+
test_line_count = 6 observed.oids &&
git -C pc1 fetch-pack --stdin "file://$(pwd)/srv.bare" <observed.oids &&
- git -C pc1 rev-list master..origin/master --quiet --objects --missing=print \
- | awk -f print_1.awk \
- | sed "s/?//" \
- | sort >observed.oids &&
+
+ git -C pc1 rev-list --quiet --objects --missing=print \
+ master..origin/master >revs &&
+ awk -f print_1.awk revs |
+ sed "s/?//" |
+ sort >observed.oids &&
+
test_line_count = 0 observed.oids
'
@@ -170,6 +182,23 @@ test_expect_success 'partial clone fetches blobs pointed to by refs even if norm
git -C dst fsck
'
+test_expect_success 'fetch what is specified on CLI even if already promised' '
+ rm -rf src dst.git &&
+ git init src &&
+ test_commit -C src foo &&
+ test_config -C src uploadpack.allowfilter 1 &&
+ test_config -C src uploadpack.allowanysha1inwant 1 &&
+
+ git hash-object --stdin <src/foo.t >blob &&
+
+ git clone --bare --filter=blob:none "file://$(pwd)/src" dst.git &&
+ git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_before &&
+ grep "?$(cat blob)" missing_before &&
+ git -C dst.git fetch origin $(cat blob) &&
+ git -C dst.git rev-list --objects --quiet --missing=print HEAD >missing_after &&
+ ! grep "?$(cat blob)" missing_after
+'
+
. "$TEST_DIRECTORY"/lib-httpd.sh
start_httpd
@@ -194,7 +223,7 @@ test_expect_success 'upon cloning, check that all refs point to objects' '
# Craft a packfile not including that blob.
git -C "$SERVER" rev-parse HEAD |
- git -C "$SERVER" pack-objects --stdout >incomplete.pack &&
+ git -C "$SERVER" pack-objects --stdout >incomplete.pack &&
# Replace the existing packfile with the crafted one. The protocol
# requires that the packfile be sent in sideband 1, hence the extra
diff --git a/t/t5701-git-serve.sh b/t/t5701-git-serve.sh
index 29a54fbfff..ae79c6bbc0 100755
--- a/t/t5701-git-serve.sh
+++ b/t/t5701-git-serve.sh
@@ -16,7 +16,7 @@ test_expect_success 'test capability advertisement' '
git serve --advertise-capabilities >out &&
test-tool pkt-line unpack <out >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'stateless-rpc flag does not list capabilities' '
@@ -89,7 +89,7 @@ test_expect_success 'basics of ls-refs' '
git serve --stateless-rpc <in >out &&
test-tool pkt-line unpack <out >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'basic ref-prefixes' '
@@ -109,7 +109,7 @@ test_expect_success 'basic ref-prefixes' '
git serve --stateless-rpc <in >out &&
test-tool pkt-line unpack <out >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'refs/heads prefix' '
@@ -129,7 +129,7 @@ test_expect_success 'refs/heads prefix' '
git serve --stateless-rpc <in >out &&
test-tool pkt-line unpack <out >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'peel parameter' '
@@ -150,7 +150,7 @@ test_expect_success 'peel parameter' '
git serve --stateless-rpc <in >out &&
test-tool pkt-line unpack <out >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'symrefs parameter' '
@@ -171,7 +171,7 @@ test_expect_success 'symrefs parameter' '
git serve --stateless-rpc <in >out &&
test-tool pkt-line unpack <out >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'sending server-options' '
@@ -191,7 +191,7 @@ test_expect_success 'sending server-options' '
git serve --stateless-rpc <in >out &&
test-tool pkt-line unpack <out >actual &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'unexpected lines are not allowed in fetch request' '
diff --git a/t/t5702-protocol-v2.sh b/t/t5702-protocol-v2.sh
index 88a886975d..8360188c01 100755
--- a/t/t5702-protocol-v2.sh
+++ b/t/t5702-protocol-v2.sh
@@ -29,7 +29,7 @@ test_expect_success 'list refs with git:// using protocol v2' '
grep "git< version 2" log &&
git ls-remote --symref "$GIT_DAEMON_URL/parent" >expect &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'ref advertisment is filtered with ls-remote using protocol v2' '
@@ -42,7 +42,7 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol
$(git -C "$daemon_parent" rev-parse refs/heads/master)$(printf "\t")refs/heads/master
EOF
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'clone with git:// using protocol v2' '
@@ -79,6 +79,19 @@ test_expect_success 'fetch with git:// using protocol v2' '
grep "fetch< version 2" log
'
+test_expect_success 'fetch by hash without tag following with protocol v2 does not list refs' '
+ test_when_finished "rm -f log" &&
+
+ test_commit -C "$daemon_parent" two_a &&
+ git -C "$daemon_parent" rev-parse two_a >two_a_hash &&
+
+ GIT_TRACE_PACKET="$(pwd)/log" git -C daemon_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 'pull with git:// using protocol v2' '
test_when_finished "rm -f log" &&
@@ -138,7 +151,7 @@ test_expect_success 'list refs with file:// using protocol v2' '
grep "git< version 2" log &&
git ls-remote --symref "file://$(pwd)/file_parent" >expect &&
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'ref advertisment is filtered with ls-remote using protocol v2' '
@@ -151,7 +164,7 @@ test_expect_success 'ref advertisment is filtered with ls-remote using protocol
$(git -C file_parent rev-parse refs/heads/master)$(printf "\t")refs/heads/master
EOF
- test_cmp actual expect
+ test_cmp expect actual
'
test_expect_success 'server-options are sent when using ls-remote' '
@@ -164,7 +177,7 @@ test_expect_success 'server-options are sent when using ls-remote' '
$(git -C file_parent rev-parse refs/heads/master)$(printf "\t")refs/heads/master
EOF
- test_cmp actual expect &&
+ test_cmp expect actual &&
grep "server-option=hello" log &&
grep "server-option=world" log
'
@@ -271,7 +284,7 @@ test_expect_success 'partial clone' '
grep "version 2" trace &&
# Ensure that the old version of the file is missing
- git -C client rev-list master --quiet --objects --missing=print \
+ git -C client rev-list --quiet --objects --missing=print master \
>observed.oids &&
grep "$(git -C server rev-parse message1:a.txt)" observed.oids &&
@@ -286,6 +299,10 @@ test_expect_success 'dynamically fetch missing object' '
grep "version 2" trace
'
+test_expect_success 'when dynamically fetching missing object, do not list refs' '
+ ! grep "git> command=ls-refs" trace
+'
+
test_expect_success 'partial fetch' '
rm -rf client "$(pwd)/trace" &&
git init client &&
@@ -297,7 +314,7 @@ test_expect_success 'partial fetch' '
grep "version 2" trace &&
# Ensure that the old version of the file is missing
- git -C client rev-list other --quiet --objects --missing=print \
+ git -C client rev-list --quiet --objects --missing=print other \
>observed.oids &&
grep "$(git -C server rev-parse message1:a.txt)" observed.oids &&
diff --git a/t/t6023-merge-file.sh b/t/t6023-merge-file.sh
index 20aee43f95..51ee887a77 100755
--- a/t/t6023-merge-file.sh
+++ b/t/t6023-merge-file.sh
@@ -99,7 +99,7 @@ EOF
printf "propter nomen suum." >> expect.txt
test_expect_success "merge does not add LF away of change" \
- "test_cmp test3.txt expect.txt"
+ "test_cmp expect.txt test3.txt"
cp test.txt backup.txt
test_expect_success "merge with conflicts" \
@@ -122,7 +122,7 @@ non timebo mala, quoniam tu mecum es:
virga tua et baculus tuus ipsa me consolata sunt.
EOF
-test_expect_success "expected conflict markers" "test_cmp test.txt expect.txt"
+test_expect_success "expected conflict markers" "test_cmp expect.txt test.txt"
cp backup.txt test.txt
@@ -138,7 +138,7 @@ non timebo mala, quoniam tu mecum es:
virga tua et baculus tuus ipsa me consolata sunt.
EOF
test_expect_success "merge conflicting with --ours" \
- "git merge-file --ours test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+ "git merge-file --ours test.txt orig.txt new3.txt && test_cmp expect.txt test.txt"
cp backup.txt test.txt
cat > expect.txt << EOF
@@ -154,7 +154,7 @@ non timebo mala, quoniam tu mecum es:
virga tua et baculus tuus ipsa me consolata sunt.
EOF
test_expect_success "merge conflicting with --theirs" \
- "git merge-file --theirs test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+ "git merge-file --theirs test.txt orig.txt new3.txt && test_cmp expect.txt test.txt"
cp backup.txt test.txt
cat > expect.txt << EOF
@@ -171,7 +171,7 @@ non timebo mala, quoniam tu mecum es:
virga tua et baculus tuus ipsa me consolata sunt.
EOF
test_expect_success "merge conflicting with --union" \
- "git merge-file --union test.txt orig.txt new3.txt && test_cmp test.txt expect.txt"
+ "git merge-file --union test.txt orig.txt new3.txt && test_cmp expect.txt test.txt"
cp backup.txt test.txt
test_expect_success "merge with conflicts, using -L" \
@@ -195,7 +195,7 @@ virga tua et baculus tuus ipsa me consolata sunt.
EOF
test_expect_success "expected conflict markers, with -L" \
- "test_cmp test.txt expect.txt"
+ "test_cmp expect.txt test.txt"
sed "s/ tu / TU /" < new1.txt > new5.txt
test_expect_success "conflict in removed tail" \
diff --git a/t/t6027-merge-binary.sh b/t/t6027-merge-binary.sh
index 07735410b9..4e6c7cb77e 100755
--- a/t/t6027-merge-binary.sh
+++ b/t/t6027-merge-binary.sh
@@ -45,7 +45,7 @@ test_expect_success resolve '
false
else
git ls-files -s >current
- test_cmp current expect
+ test_cmp expect current
fi
'
@@ -60,7 +60,7 @@ test_expect_success recursive '
false
else
git ls-files -s >current
- test_cmp current expect
+ test_cmp expect current
fi
'
diff --git a/t/t6031-merge-filemode.sh b/t/t6031-merge-filemode.sh
index 7d06461f13..87741efad3 100755
--- a/t/t6031-merge-filemode.sh
+++ b/t/t6031-merge-filemode.sh
@@ -61,7 +61,7 @@ do_both_modes () {
git checkout -f a2 &&
test_must_fail git merge -s $strategy b2 &&
git ls-files -u >actual &&
- test_cmp actual expect &&
+ test_cmp expect actual &&
git ls-files -s file2 | grep ^100755
'
diff --git a/t/t6112-rev-list-filters-objects.sh b/t/t6112-rev-list-filters-objects.sh
index d4ff0b3bef..53975c5724 100755
--- a/t/t6112-rev-list-filters-objects.sh
+++ b/t/t6112-rev-list-filters-objects.sh
@@ -21,24 +21,31 @@ test_expect_success 'setup r1' '
test_expect_success 'verify blob:none omits all 5 blobs' '
git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r1 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:none \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r1 rev-list --quiet --objects --filter-print-omitted \
+ --filter=blob:none HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify emitted+omitted == all' '
- git -C r1 rev-list HEAD --objects \
- | awk -f print_1.awk \
- | sort >expected &&
- git -C r1 rev-list HEAD --objects --filter-print-omitted --filter=blob:none \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r1 rev-list --objects HEAD >revs &&
+ awk -f print_1.awk revs |
+ sort >expected &&
+
+ git -C r1 rev-list --objects --filter-print-omitted --filter=blob:none \
+ HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
@@ -58,65 +65,82 @@ test_expect_success 'setup r2' '
'
test_expect_success 'verify blob:limit=500 omits all blobs' '
- git -C r2 ls-files -s large.1000 large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=500 \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r2 rev-list --quiet --objects --filter-print-omitted \
+ --filter=blob:limit=500 HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify emitted+omitted == all' '
- git -C r2 rev-list HEAD --objects \
- | awk -f print_1.awk \
- | sort >expected &&
- git -C r2 rev-list HEAD --objects --filter-print-omitted --filter=blob:limit=500 \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r2 rev-list --objects HEAD >revs &&
+ awk -f print_1.awk revs |
+ sort >expected &&
+
+ git -C r2 rev-list --objects --filter-print-omitted \
+ --filter=blob:limit=500 HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=1000' '
- git -C r2 ls-files -s large.1000 large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1000 \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r2 ls-files -s large.1000 large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r2 rev-list --quiet --objects --filter-print-omitted \
+ --filter=blob:limit=1000 HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=1001' '
- git -C r2 ls-files -s large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1001 \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r2 ls-files -s large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r2 rev-list --quiet --objects --filter-print-omitted \
+ --filter=blob:limit=1001 HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=1k' '
- git -C r2 ls-files -s large.10000 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1k \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r2 ls-files -s large.10000 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r2 rev-list --quiet --objects --filter-print-omitted \
+ --filter=blob:limit=1k HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify blob:limit=1m' '
- git -C r2 rev-list HEAD --quiet --objects --filter-print-omitted --filter=blob:limit=1m \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
+ git -C r2 rev-list --quiet --objects --filter-print-omitted \
+ --filter=blob:limit=1m HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
test_must_be_empty observed
'
@@ -141,25 +165,31 @@ test_expect_success 'setup r3' '
'
test_expect_success 'verify sparse:path=pattern1 omits top-level files' '
- git -C r3 ls-files -s sparse1 sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:path=../pattern1 \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r3 ls-files -s sparse1 sparse2 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r3 rev-list --quiet --objects --filter-print-omitted \
+ --filter=sparse:path=../pattern1 HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify sparse:path=pattern2 omits both sparse2 files' '
- git -C r3 ls-files -s sparse2 dir1/sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:path=../pattern2 \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r3 ls-files -s sparse2 dir1/sparse2 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r3 rev-list --quiet --objects --filter-print-omitted \
+ --filter=sparse:path=../pattern2 HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
# Test sparse:oid=<oid-ish> filter.
@@ -173,26 +203,33 @@ test_expect_success 'setup r3 part 2' '
'
test_expect_success 'verify sparse:oid=OID omits top-level files' '
- git -C r3 ls-files -s pattern sparse1 sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
+ git -C r3 ls-files -s pattern sparse1 sparse2 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
oid=$(git -C r3 ls-files -s pattern | awk -f print_2.awk) &&
- git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:oid=$oid \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r3 rev-list --quiet --objects --filter-print-omitted \
+ --filter=sparse:oid=$oid HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'verify sparse:oid=oid-ish omits top-level files' '
- git -C r3 ls-files -s pattern sparse1 sparse2 \
- | awk -f print_2.awk \
- | sort >expected &&
- git -C r3 rev-list HEAD --quiet --objects --filter-print-omitted --filter=sparse:oid=master:pattern \
- | awk -f print_1.awk \
- | sed "s/~//" \
- | sort >observed &&
- test_cmp observed expected
+ git -C r3 ls-files -s pattern sparse1 sparse2 >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
+ git -C r3 rev-list --quiet --objects --filter-print-omitted \
+ --filter=sparse:oid=master:pattern HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/~//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
# Delete some loose objects and use rev-list, but WITHOUT any filtering.
@@ -200,17 +237,21 @@ test_expect_success 'verify sparse:oid=oid-ish omits top-level files' '
test_expect_success 'rev-list W/ --missing=print' '
git -C r1 ls-files -s file.1 file.2 file.3 file.4 file.5 \
- | awk -f print_2.awk \
- | sort >expected &&
+ >ls_files_result &&
+ awk -f print_2.awk ls_files_result |
+ sort >expected &&
+
for id in `cat expected | sed "s|..|&/|"`
do
rm r1/.git/objects/$id
done &&
- git -C r1 rev-list --quiet HEAD --missing=print --objects \
- | awk -f print_1.awk \
- | sed "s/?//" \
- | sort >observed &&
- test_cmp observed expected
+
+ git -C r1 rev-list --quiet --missing=print --objects HEAD >revs &&
+ awk -f print_1.awk revs |
+ sed "s/?//" |
+ sort >observed &&
+
+ test_cmp expected observed
'
test_expect_success 'rev-list W/O --missing fails' '
diff --git a/t/t6500-gc.sh b/t/t6500-gc.sh
index 818435f04e..4684d06552 100755
--- a/t/t6500-gc.sh
+++ b/t/t6500-gc.sh
@@ -4,6 +4,7 @@ test_description='basic git gc tests
'
. ./test-lib.sh
+. "$TEST_DIRECTORY"/lib-terminal.sh
test_expect_success 'setup' '
# do not let the amount of physical memory affects gc
@@ -99,6 +100,26 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
test_line_count = 2 new # There is one new pack and its .idx
'
+test_expect_success 'gc --no-quiet' '
+ git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
+ test_must_be_empty stdout &&
+ test_line_count = 1 stderr &&
+ test_i18ngrep "Computing commit graph generation numbers" stderr
+'
+
+test_expect_success TTY 'with TTY: gc --no-quiet' '
+ test_terminal git -c gc.writeCommitGraph=true gc --no-quiet >stdout 2>stderr &&
+ test_must_be_empty stdout &&
+ test_i18ngrep "Enumerating objects" stderr &&
+ test_i18ngrep "Computing commit graph generation numbers" stderr
+'
+
+test_expect_success 'gc --quiet' '
+ git -c gc.writeCommitGraph=true gc --quiet >stdout 2>stderr &&
+ test_must_be_empty stdout &&
+ test_must_be_empty stderr
+'
+
run_and_wait_for_auto_gc () {
# We read stdout from gc for the side effect of waiting until the
# background gc process exits, closing its fd 9. Furthermore, the
@@ -116,11 +137,11 @@ test_expect_success 'background auto gc does not run if gc.log is present and re
test_config gc.autopacklimit 1 &&
test_config gc.autodetach true &&
echo fleem >.git/gc.log &&
- test_must_fail git gc --auto 2>err &&
- test_i18ngrep "^error:" err &&
+ git gc --auto 2>err &&
+ test_i18ngrep "^warning:" err &&
test_config gc.logexpiry 5.days &&
test-tool chmtime =-345600 .git/gc.log &&
- test_must_fail git gc --auto &&
+ git gc --auto &&
test_config gc.logexpiry 2.days &&
run_and_wait_for_auto_gc &&
ls .git/objects/pack/pack-*.pack >packs &&
diff --git a/t/t7005-editor.sh b/t/t7005-editor.sh
index b2ca77b338..5fcf281dfb 100755
--- a/t/t7005-editor.sh
+++ b/t/t7005-editor.sh
@@ -112,7 +112,7 @@ do
done
test_expect_success 'editor with a space' '
- echo "echo space >\$1" >"e space.sh" &&
+ echo "echo space >\"\$1\"" >"e space.sh" &&
chmod a+x "e space.sh" &&
GIT_EDITOR="./e\ space.sh" git commit --amend &&
test space = "$(git show -s --pretty=format:%s)"
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 324933acfe..826987ca80 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -160,7 +160,7 @@ test_expect_success 'checkout -m with merge conflict' '
git diff master:one :3:uno |
sed -e "1,/^@@/d" -e "/^ /d" -e "s/^-/d/" -e "s/^+/a/" >current &&
fill d2 aT d7 aS >expect &&
- test_cmp current expect &&
+ test_cmp expect current &&
git diff --cached two >current &&
test_must_be_empty current
'
@@ -174,7 +174,7 @@ test_expect_success 'format of merge conflict from checkout -m' '
git ls-files >current &&
fill same two two two >expect &&
- test_cmp current expect &&
+ test_cmp expect current &&
cat <<-EOF >expect &&
<<<<<<< simple
diff --git a/t/t7406-submodule-update.sh b/t/t7406-submodule-update.sh
index 10dc91620a..e87164aa8f 100755
--- a/t/t7406-submodule-update.sh
+++ b/t/t7406-submodule-update.sh
@@ -789,7 +789,7 @@ test_expect_success 'submodule add places git-dir in superprojects git-dir' '
(cd .git/modules/deeper/submodule &&
git log > ../../../../actual
) &&
- test_cmp actual expected
+ test_cmp expected actual
)
'
@@ -807,7 +807,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir' '
(cd .git/modules/deeper/submodule &&
git log > ../../../../actual
) &&
- test_cmp actual expected
+ test_cmp expected actual
)
'
@@ -827,7 +827,7 @@ test_expect_success 'submodule add places git-dir in superprojects git-dir recur
git add deeper/submodule &&
git commit -m "update submodule" &&
git push origin : &&
- test_cmp actual expected
+ test_cmp expected actual
)
'
@@ -874,7 +874,7 @@ test_expect_success 'submodule update places git-dir in superprojects git-dir re
(cd .git/modules/submodule/modules/subsubmodule &&
git log > ../../../../../actual
) &&
- test_cmp actual expected
+ test_cmp expected actual
)
'
diff --git a/t/t7500-commit.sh b/t/t7500-commit.sh
index 170b4810e0..31ab608b67 100755
--- a/t/t7500-commit.sh
+++ b/t/t7500-commit.sh
@@ -359,4 +359,27 @@ test_expect_success 'new line found before status message in commit template' '
test_i18ncmp expected-template editor-input
'
+test_expect_success 'setup empty commit with unstaged rename and copy' '
+ test_create_repo unstaged_rename_and_copy &&
+ (
+ cd unstaged_rename_and_copy &&
+
+ echo content >orig &&
+ git add orig &&
+ test_commit orig &&
+
+ cp orig new_copy &&
+ mv orig new_rename &&
+ git add -N new_copy new_rename
+ )
+'
+
+test_expect_success 'check commit with unstaged rename and copy' '
+ (
+ cd unstaged_rename_and_copy &&
+
+ test_must_fail git -c diff.renames=copy commit
+ )
+'
+
test_done
diff --git a/t/t7501-commit.sh b/t/t7501-commit.sh
index 1a6773ee68..f1349af56e 100755
--- a/t/t7501-commit.sh
+++ b/t/t7501-commit.sh
@@ -99,12 +99,12 @@ test_expect_success '--dry-run with stuff to commit returns ok' '
git commit -m next -a --dry-run
'
-test_expect_failure '--short with stuff to commit returns ok' '
+test_expect_success '--short with stuff to commit returns ok' '
echo bongo bongo bongo >>file &&
git commit -m next -a --short
'
-test_expect_failure '--porcelain with stuff to commit returns ok' '
+test_expect_success '--porcelain with stuff to commit returns ok' '
echo bongo bongo bongo >>file &&
git commit -m next -a --porcelain
'
@@ -698,4 +698,10 @@ test_expect_success '--dry-run with conflicts fixed from a merge' '
git commit -m "conflicts fixed from merge."
'
+test_expect_success '--dry-run --short' '
+ >test-file &&
+ git add test-file &&
+ git commit --dry-run --short
+'
+
test_done
diff --git a/t/t7519-status-fsmonitor.sh b/t/t7519-status-fsmonitor.sh
index 8384ad258c..3e0a61db23 100755
--- a/t/t7519-status-fsmonitor.sh
+++ b/t/t7519-status-fsmonitor.sh
@@ -4,13 +4,6 @@ test_description='git status with file system watcher'
. ./test-lib.sh
-#
-# To run the entire git test suite using fsmonitor:
-#
-# copy t/t7519/fsmonitor-all to a location in your path and then set
-# GIT_FSMONITOR_TEST=fsmonitor-all and run your tests.
-#
-
# Note, after "git reset --hard HEAD" no extensions exist other than 'TREE'
# "git update-index --fsmonitor" can be used to get the extension written
# before testing the results.
@@ -245,9 +238,9 @@ do
git config core.preloadIndex $preload_val &&
if test $preload_val = true
then
- GIT_FORCE_PRELOAD_TEST=$preload_val; export GIT_FORCE_PRELOAD_TEST
+ GIT_TEST_PRELOAD_INDEX=$preload_val; export GIT_TEST_PRELOAD_INDEX
else
- unset GIT_FORCE_PRELOAD_TEST
+ sane_unset GIT_TEST_PRELOAD_INDEX
fi
'
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 668bbee73c..562bd215a5 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -557,7 +557,7 @@ test_expect_success SYMLINKS 'difftool --dir-diff --symlink without unstaged cha
EOF
git difftool --dir-diff --symlink \
--extcmd "./.git/CHECK_SYMLINKS" branch HEAD &&
- test_cmp actual expect
+ test_cmp expect actual
'
write_script modify-right-file <<\EOF
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index be5c1bd553..43aa4161cf 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -309,6 +309,8 @@ do
echo ${HC}v:1:vvv
} >expected &&
git grep --max-depth -1 -n -e vvv $H >actual &&
+ test_cmp expected actual &&
+ git grep --recursive -n -e vvv $H >actual &&
test_cmp expected actual
'
@@ -317,6 +319,8 @@ do
echo ${HC}v:1:vvv
} >expected &&
git grep --max-depth 0 -n -e vvv $H >actual &&
+ test_cmp expected actual &&
+ git grep --no-recursive -n -e vvv $H >actual &&
test_cmp expected actual
'
@@ -327,6 +331,8 @@ do
echo ${HC}v:1:vvv
} >expected &&
git grep --max-depth 0 -n -e vvv $H -- "*" >actual &&
+ test_cmp expected actual &&
+ git grep --no-recursive -n -e vvv $H -- "*" >actual &&
test_cmp expected actual
'
@@ -344,6 +350,8 @@ do
echo ${HC}t/v:1:vvv
} >expected &&
git grep --max-depth 0 -n -e vvv $H -- t >actual &&
+ test_cmp expected actual &&
+ git grep --no-recursive -n -e vvv $H -- t >actual &&
test_cmp expected actual
'
@@ -353,6 +361,8 @@ do
echo ${HC}v:1:vvv
} >expected &&
git grep --max-depth 0 -n -e vvv $H -- . t >actual &&
+ test_cmp expected actual &&
+ git grep --no-recursive -n -e vvv $H -- . t >actual &&
test_cmp expected actual
'
@@ -362,6 +372,8 @@ do
echo ${HC}v:1:vvv
} >expected &&
git grep --max-depth 0 -n -e vvv $H -- t . >actual &&
+ test_cmp expected actual &&
+ git grep --no-recursive -n -e vvv $H -- t . >actual &&
test_cmp expected actual
'
test_expect_success "grep $L with grep.extendedRegexp=false" '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index 9af6078844..2c309a57d9 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -221,7 +221,7 @@ tree d667270a1f7b109f5eb3aaea21ede14b56bfdd6e
tree 8f51f74cf0163afc9ad68a4b1537288c4558b5a4
EOF
-test_expect_success POSIXPERM,SYMLINKS "$name" "test_cmp a expected"
+test_expect_success POSIXPERM,SYMLINKS "$name" "test_cmp expected a"
test_expect_success 'exit if remote refs are ambigious' '
git config --add svn-remote.svn.fetch \
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8a5c8dc1aa..c26c4b0927 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -174,7 +174,8 @@ test_expect_success 'test create-ignore' "
cmp ./deeply/.gitignore create-ignore.expect &&
cmp ./deeply/nested/.gitignore create-ignore.expect &&
cmp ./deeply/nested/directory/.gitignore create-ignore.expect &&
- git ls-files -s | grep gitignore | cmp - create-ignore-index.expect
+ git ls-files -s >ls_files_result &&
+ grep gitignore ls_files_result | cmp - create-ignore-index.expect
"
cat >prop.expect <<\EOF
@@ -189,17 +190,21 @@ EOF
# This test can be improved: since all the svn:ignore contain the same
# pattern, it can pass even though the propget did not execute on the
# right directory.
-test_expect_success 'test propget' "
- git svn propget svn:ignore . | cmp - prop.expect &&
+test_expect_success 'test propget' '
+ test_propget () {
+ git svn propget $1 $2 >actual &&
+ cmp $3 actual
+ } &&
+ test_propget svn:ignore . prop.expect &&
cd deeply &&
- git svn propget svn:ignore . | cmp - ../prop.expect &&
- git svn propget svn:entry:committed-rev nested/directory/.keep \
- | cmp - ../prop2.expect &&
- git svn propget svn:ignore .. | cmp - ../prop.expect &&
- git svn propget svn:ignore nested/ | cmp - ../prop.expect &&
- git svn propget svn:ignore ./nested | cmp - ../prop.expect &&
- git svn propget svn:ignore .././deeply/nested | cmp - ../prop.expect
- "
+ test_propget svn:ignore . ../prop.expect &&
+ test_propget svn:entry:committed-rev nested/directory/.keep \
+ ../prop2.expect &&
+ test_propget svn:ignore .. ../prop.expect &&
+ test_propget svn:ignore nested/ ../prop.expect &&
+ test_propget svn:ignore ./nested ../prop.expect &&
+ test_propget svn:ignore .././deeply/nested ../prop.expect
+ '
cat >prop.expect <<\EOF
Properties on '.':
@@ -218,8 +223,11 @@ Properties on 'nested/directory/.keep':
EOF
test_expect_success 'test proplist' "
- git svn proplist . | cmp - prop.expect &&
- git svn proplist nested/directory/.keep | cmp - prop2.expect
+ git svn proplist . >actual &&
+ cmp prop.expect actual &&
+
+ git svn proplist nested/directory/.keep >actual &&
+ cmp prop2.expect actual
"
test_done
diff --git a/t/t9133-git-svn-nested-git-repo.sh b/t/t9133-git-svn-nested-git-repo.sh
index f3c30e63b7..f894860867 100755
--- a/t/t9133-git-svn-nested-git-repo.sh
+++ b/t/t9133-git-svn-nested-git-repo.sh
@@ -45,7 +45,7 @@ test_expect_success 'update git svn-cloned repo' '
git svn rebase &&
echo a > expect &&
echo b >> expect &&
- test_cmp a expect &&
+ test_cmp expect a &&
rm expect
)
'
@@ -69,7 +69,7 @@ test_expect_success 'update git svn-cloned repo' '
git svn rebase &&
echo a > expect &&
echo b >> expect &&
- test_cmp a expect &&
+ test_cmp expect a &&
rm expect
)
'
@@ -93,7 +93,7 @@ test_expect_success 'update git svn-cloned repo again' '
echo a > expect &&
echo b >> expect &&
echo c >> expect &&
- test_cmp a expect &&
+ test_cmp expect a &&
rm expect
)
'
diff --git a/t/t9600-cvsimport.sh b/t/t9600-cvsimport.sh
index 5dfee07d9a..251fdd66c4 100755
--- a/t/t9600-cvsimport.sh
+++ b/t/t9600-cvsimport.sh
@@ -148,7 +148,7 @@ test_expect_success PERL 'import from a CVS working tree' '
git cvsimport -a -z0 &&
echo 1 >expect &&
git log -1 --pretty=format:%s%n >actual &&
- test_cmp actual expect
+ test_cmp expect actual
)
'
diff --git a/t/t9603-cvsimport-patchsets.sh b/t/t9603-cvsimport-patchsets.sh
index c4c3c49546..3e64b11eac 100755
--- a/t/t9603-cvsimport-patchsets.sh
+++ b/t/t9603-cvsimport-patchsets.sh
@@ -29,11 +29,11 @@ test_expect_failure PERL 'import with criss cross times on revisions' '
Rev 3
Rev 2
Rev 1" > expect-master &&
- test_cmp actual-master expect-master &&
+ test_cmp expect-master actual-master &&
echo "Rev 5 Branch A Wed Mar 11 19:09:10 2009 +0000
Rev 4 Branch A Wed Mar 11 19:03:52 2009 +0000" > expect-A &&
- test_cmp actual-A expect-A
+ test_cmp expect-A actual-A
'
test_done
diff --git a/t/t9604-cvsimport-timestamps.sh b/t/t9604-cvsimport-timestamps.sh
index a4b3db24bd..2ff4aa932d 100755
--- a/t/t9604-cvsimport-timestamps.sh
+++ b/t/t9604-cvsimport-timestamps.sh
@@ -31,7 +31,7 @@ test_expect_success PERL 'check timestamps are UTC (TZ=CST6CDT)' '
Rev 2 2005-02-01 00:00:00 +0000
Rev 1 2005-01-01 00:00:00 +0000
EOF
- test_cmp actual-1 expect-1
+ test_cmp expect-1 actual-1
'
test_expect_success PERL 'check timestamps with author-specific timezones' '
@@ -65,7 +65,7 @@ test_expect_success PERL 'check timestamps with author-specific timezones' '
Rev 2 2005-01-31 18:00:00 -0600 User Two
Rev 1 2005-01-01 00:00:00 +0000 User One
EOF
- test_cmp actual-2 expect-2
+ test_cmp expect-2 actual-2
'
test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 3f95bfda60..897e6fcc94 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -134,9 +134,40 @@ export EDITOR
GIT_TRACE_BARE=1
export GIT_TRACE_BARE
-if test -n "${TEST_GIT_INDEX_VERSION:+isset}"
+check_var_migration () {
+ # the warnings and hints given from this helper depends
+ # on end-user settings, which will disrupt the self-test
+ # done on the test framework itself.
+ case "$GIT_TEST_FRAMEWORK_SELFTEST" in
+ t) return ;;
+ esac
+
+ old_name=$1 new_name=$2
+ eval "old_isset=\${${old_name}:+isset}"
+ eval "new_isset=\${${new_name}:+isset}"
+
+ case "$old_isset,$new_isset" in
+ isset,)
+ echo >&2 "warning: $old_name is now $new_name"
+ echo >&2 "hint: set $new_name too during the transition period"
+ eval "$new_name=\$$old_name"
+ ;;
+ isset,isset)
+ # do this later
+ # echo >&2 "warning: $old_name is now $new_name"
+ # echo >&2 "hint: remove $old_name"
+ ;;
+ esac
+}
+
+check_var_migration GIT_FSMONITOR_TEST GIT_TEST_FSMONITOR
+check_var_migration TEST_GIT_INDEX_VERSION GIT_TEST_INDEX_VERSION
+check_var_migration GIT_FORCE_PRELOAD_TEST GIT_TEST_PRELOAD_INDEX
+
+# Use specific version of the index file format
+if test -n "${GIT_TEST_INDEX_VERSION:+isset}"
then
- GIT_INDEX_VERSION="$TEST_GIT_INDEX_VERSION"
+ GIT_INDEX_VERSION="$GIT_TEST_INDEX_VERSION"
export GIT_INDEX_VERSION
fi
diff --git a/transport-helper.c b/transport-helper.c
index 143ca008c8..7213fa0d32 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1105,6 +1105,7 @@ 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,
diff --git a/transport-internal.h b/transport-internal.h
index 1cde6258a7..004bee5e36 100644
--- a/transport-internal.h
+++ b/transport-internal.h
@@ -7,6 +7,12 @@ 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
* is applicable but the value is invalid.
diff --git a/transport.c b/transport.c
index 1c76d64aba..f4ffbd96cb 100644
--- a/transport.c
+++ b/transport.c
@@ -252,8 +252,18 @@ static int connect_setup(struct transport *transport, int for_push)
return 0;
}
-static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
- const struct argv_array *ref_prefixes)
+/*
+ * Obtains the protocol version from the transport and writes it to
+ * transport->data->version, first connecting if not already connected.
+ *
+ * If the protocol version is one that allows skipping the listing of remote
+ * refs, and must_list_refs is 0, the listing of remote refs is skipped and
+ * this function returns NULL. Otherwise, this function returns the list of
+ * remote refs.
+ */
+static struct ref *handshake(struct transport *transport, int for_push,
+ const struct argv_array *ref_prefixes,
+ int must_list_refs)
{
struct git_transport_data *data = transport->data;
struct ref *refs = NULL;
@@ -268,8 +278,10 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
data->version = discover_version(&reader);
switch (data->version) {
case protocol_v2:
- get_remote_refs(data->fd[1], &reader, &refs, for_push,
- ref_prefixes, transport->server_options);
+ if (must_list_refs)
+ get_remote_refs(data->fd[1], &reader, &refs, for_push,
+ ref_prefixes,
+ transport->server_options);
break;
case protocol_v1:
case protocol_v0:
@@ -283,9 +295,18 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
}
data->got_remote_heads = 1;
+ if (reader.line_peeked)
+ BUG("buffer must be empty at the end of handshake()");
+
return refs;
}
+static struct ref *get_refs_via_connect(struct transport *transport, int for_push,
+ const struct argv_array *ref_prefixes)
+{
+ return handshake(transport, for_push, ref_prefixes, 1);
+}
+
static int fetch_refs_via_pack(struct transport *transport,
int nr_heads, struct ref **to_fetch)
{
@@ -320,8 +341,17 @@ static int fetch_refs_via_pack(struct transport *transport,
args.server_options = transport->server_options;
args.negotiation_tips = data->options.negotiation_tips;
- if (!data->got_remote_heads)
- refs_tmp = get_refs_via_connect(transport, 0, NULL);
+ if (!data->got_remote_heads) {
+ int i;
+ int must_list_refs = 0;
+ for (i = 0; i < nr_heads; i++) {
+ if (!to_fetch[i]->exact_oid) {
+ must_list_refs = 1;
+ break;
+ }
+ }
+ refs_tmp = handshake(transport, 0, NULL, must_list_refs);
+ }
switch (data->version) {
case protocol_v2:
@@ -703,6 +733,7 @@ static int disconnect_git(struct transport *transport)
}
static struct transport_vtable taken_over_vtable = {
+ 1,
NULL,
get_refs_via_connect,
fetch_refs_via_pack,
@@ -852,6 +883,7 @@ void transport_check_allowed(const char *type)
}
static struct transport_vtable bundle_vtable = {
+ 0,
NULL,
get_refs_from_bundle,
fetch_refs_from_bundle,
@@ -861,6 +893,7 @@ static struct transport_vtable bundle_vtable = {
};
static struct transport_vtable builtin_smart_vtable = {
+ 1,
NULL,
get_refs_via_connect,
fetch_refs_via_pack,
@@ -1139,7 +1172,8 @@ int transport_push(struct transport *transport,
oid_array_append(&commits,
&ref->new_oid);
- if (!push_unpushed_submodules(&commits,
+ if (!push_unpushed_submodules(&the_index,
+ &commits,
transport->remote,
rs,
transport->push_options,
@@ -1163,8 +1197,10 @@ int transport_push(struct transport *transport,
oid_array_append(&commits,
&ref->new_oid);
- if (find_unpushed_submodules(&commits, transport->remote->name,
- &needs_pushing)) {
+ if (find_unpushed_submodules(&the_index,
+ &commits,
+ transport->remote->name,
+ &needs_pushing)) {
oid_array_clear(&commits);
die_with_unpushed_submodules(&needs_pushing);
}
@@ -1224,6 +1260,15 @@ 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 &&
@@ -1325,6 +1370,33 @@ literal_copy:
return xstrdup(url);
}
+static void fill_alternate_refs_command(struct child_process *cmd,
+ const char *repo_path)
+{
+ const char *value;
+
+ if (!git_config_get_value("core.alternateRefsCommand", &value)) {
+ cmd->use_shell = 1;
+
+ argv_array_push(&cmd->args, value);
+ argv_array_push(&cmd->args, repo_path);
+ } else {
+ cmd->git_cmd = 1;
+
+ argv_array_pushf(&cmd->args, "--git-dir=%s", repo_path);
+ argv_array_push(&cmd->args, "for-each-ref");
+ argv_array_push(&cmd->args, "--format=%(objectname)");
+
+ if (!git_config_get_value("core.alternateRefsPrefixes", &value)) {
+ argv_array_push(&cmd->args, "--");
+ argv_array_split(&cmd->args, value);
+ }
+ }
+
+ cmd->env = local_repo_env;
+ cmd->out = -1;
+}
+
static void read_alternate_refs(const char *path,
alternate_ref_fn *cb,
void *data)
@@ -1333,12 +1405,7 @@ static void read_alternate_refs(const char *path,
struct strbuf line = STRBUF_INIT;
FILE *fh;
- cmd.git_cmd = 1;
- argv_array_pushf(&cmd.args, "--git-dir=%s", path);
- argv_array_push(&cmd.args, "for-each-ref");
- argv_array_push(&cmd.args, "--format=%(objectname) %(refname)");
- cmd.env = local_repo_env;
- cmd.out = -1;
+ fill_alternate_refs_command(&cmd, path);
if (start_command(&cmd))
return;
@@ -1348,13 +1415,13 @@ static void read_alternate_refs(const char *path,
struct object_id oid;
if (get_oid_hex(line.buf, &oid) ||
- line.buf[GIT_SHA1_HEXSZ] != ' ') {
+ line.buf[GIT_SHA1_HEXSZ]) {
warning(_("invalid line while parsing alternate refs: %s"),
line.buf);
break;
}
- cb(line.buf + GIT_SHA1_HEXSZ + 1, &oid, data);
+ cb(&oid, data);
}
fclose(fh);
diff --git a/transport.h b/transport.h
index 01e717c29e..9baeca2d7a 100644
--- a/transport.h
+++ b/transport.h
@@ -261,6 +261,6 @@ int transport_refs_pushed(struct ref *ref);
void transport_print_push_status(const char *dest, struct ref *refs,
int verbose, int porcelain, unsigned int *reject_reasons);
-typedef void alternate_ref_fn(const char *refname, const struct object_id *oid, void *);
+typedef void alternate_ref_fn(const struct object_id *oid, void *);
extern void for_each_alternate_ref(alternate_ref_fn, void *);
#endif
diff --git a/tree-diff.c b/tree-diff.c
index 425668e1e0..0e54324610 100644
--- a/tree-diff.c
+++ b/tree-diff.c
@@ -605,7 +605,7 @@ static void try_to_follow_renames(const struct object_id *old_oid,
choice = q->queue[0];
q->nr = 0;
- diff_setup(&diff_opts);
+ repo_diff_setup(opt->repo, &diff_opts);
diff_opts.flags.recursive = 1;
diff_opts.flags.find_copies_harder = 1;
diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
diff --git a/upload-pack.c b/upload-pack.c
index 62a1000f44..540778d1dd 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -24,6 +24,7 @@
#include "quote.h"
#include "upload-pack.h"
#include "serve.h"
+#include "commit-graph.h"
#include "commit-reach.h"
/* Remember to update object flag allocation in object.h */
@@ -692,6 +693,7 @@ static void deepen_by_rev_list(int ac, const char **av,
{
struct commit_list *result;
+ close_commit_graph(the_repository);
result = get_shallow_commits_by_rev_list(ac, av, SHALLOW, NOT_SHALLOW);
send_shallow(result);
free_commit_list(result);
diff --git a/userdiff.c b/userdiff.c
index f565f6731d..46d34cc2a4 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -270,7 +270,8 @@ struct userdiff_driver *userdiff_find_by_name(const char *name) {
return userdiff_find_by_namelen(name, len);
}
-struct userdiff_driver *userdiff_find_by_path(const char *path)
+struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
+ const char *path)
{
static struct attr_check *check;
@@ -278,7 +279,7 @@ struct userdiff_driver *userdiff_find_by_path(const char *path)
check = attr_check_initl("diff", NULL);
if (!path)
return NULL;
- git_check_attr(&the_index, path, check);
+ git_check_attr(istate, path, check);
if (ATTR_TRUE(check->items[0].value))
return &driver_true;
diff --git a/userdiff.h b/userdiff.h
index 2ef0ce5452..b072bfe89a 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -3,6 +3,8 @@
#include "notes-cache.h"
+struct index_state;
+
struct userdiff_funcname {
const char *pattern;
int cflags;
@@ -21,7 +23,8 @@ struct userdiff_driver {
int userdiff_config(const char *k, const char *v);
struct userdiff_driver *userdiff_find_by_name(const char *name);
-struct userdiff_driver *userdiff_find_by_path(const char *path);
+struct userdiff_driver *userdiff_find_by_path(struct index_state *istate,
+ const char *path);
/*
* Initialize any textconv-related fields in the driver and return it, or NULL
diff --git a/ws.c b/ws.c
index a64ab51e09..6e69877f25 100644
--- a/ws.c
+++ b/ws.c
@@ -3,7 +3,6 @@
*
* Copyright (c) 2007 Junio C Hamano
*/
-
#include "cache.h"
#include "attr.h"
@@ -71,7 +70,7 @@ unsigned parse_whitespace_rule(const char *string)
return rule;
}
-unsigned whitespace_rule(const char *pathname)
+unsigned whitespace_rule(struct index_state *istate, const char *pathname)
{
static struct attr_check *attr_whitespace_rule;
const char *value;
@@ -79,7 +78,7 @@ unsigned whitespace_rule(const char *pathname)
if (!attr_whitespace_rule)
attr_whitespace_rule = attr_check_initl("whitespace", NULL);
- git_check_attr(&the_index, pathname, attr_whitespace_rule);
+ git_check_attr(istate, pathname, attr_whitespace_rule);
value = attr_whitespace_rule->items[0].value;
if (ATTR_TRUE(value)) {
/* true (whitespace) */
diff --git a/wt-status.c b/wt-status.c
index 1c8746d0ea..187568a112 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -540,10 +540,12 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
/* Leave {mode,oid}_head zero for an add. */
d->mode_index = p->two->mode;
oidcpy(&d->oid_index, &p->two->oid);
+ s->committable = 1;
break;
case DIFF_STATUS_DELETED:
d->mode_head = p->one->mode;
oidcpy(&d->oid_head, &p->one->oid);
+ s->committable = 1;
/* Leave {mode,oid}_index zero for a delete. */
break;
@@ -561,6 +563,7 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
d->mode_index = p->two->mode;
oidcpy(&d->oid_head, &p->one->oid);
oidcpy(&d->oid_index, &p->two->oid);
+ s->committable = 1;
break;
case DIFF_STATUS_UNMERGED:
d->stagemask = unmerged_mask(p->two->path);
@@ -582,7 +585,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
{
struct rev_info rev;
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
setup_revisions(0, NULL, &rev, NULL);
rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
rev.diffopt.flags.dirty_submodules = 1;
@@ -607,7 +610,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
struct rev_info rev;
struct setup_revision_opt opt;
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
memset(&opt, 0, sizeof(opt));
opt.def = s->is_initial ? empty_tree_oid_hex() : s->reference;
setup_revisions(0, NULL, &rev, &opt);
@@ -665,11 +668,13 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
* code will output the stage values directly and not use the
* values in these fields.
*/
+ s->committable = 1;
} else {
d->index_status = DIFF_STATUS_ADDED;
/* Leave {mode,oid}_head zero for adds. */
d->mode_index = ce->ce_mode;
oidcpy(&d->oid_index, &ce->oid);
+ s->committable = 1;
}
}
}
@@ -724,15 +729,38 @@ static void wt_status_collect_untracked(struct wt_status *s)
s->untracked_in_ms = (getnanotime() - t_begin) / 1000000;
}
+static int has_unmerged(struct wt_status *s)
+{
+ int i;
+
+ for (i = 0; i < s->change.nr; i++) {
+ struct wt_status_change_data *d;
+ d = s->change.items[i].util;
+ if (d->stagemask)
+ return 1;
+ }
+ return 0;
+}
+
void wt_status_collect(struct wt_status *s)
{
wt_status_collect_changes_worktree(s);
-
if (s->is_initial)
wt_status_collect_changes_initial(s);
else
wt_status_collect_changes_index(s);
wt_status_collect_untracked(s);
+
+ wt_status_get_state(&s->state, s->branch && !strcmp(s->branch, "HEAD"));
+ if (s->state.merge_in_progress && !has_unmerged(s))
+ s->committable = 1;
+}
+
+void wt_status_collect_free_buffers(struct wt_status *s)
+{
+ free(s->state.branch);
+ free(s->state.onto);
+ free(s->state.detached_from);
}
static void wt_longstatus_print_unmerged(struct wt_status *s)
@@ -773,7 +801,6 @@ static void wt_longstatus_print_updated(struct wt_status *s)
continue;
if (!shown_header) {
wt_longstatus_print_cached_header(s);
- s->commitable = 1;
shown_header = 1;
}
wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);
@@ -982,7 +1009,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
int dirty_submodules;
const char *c = color(WT_STATUS_HEADER, s);
- init_revisions(&rev, NULL);
+ repo_init_revisions(the_repository, &rev, NULL);
rev.diffopt.flags.allow_textconv = 1;
rev.diffopt.ita_invisible_in_index = 1;
@@ -1008,7 +1035,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
rev.diffopt.use_color = 0;
wt_status_add_cut_line(s->fp);
}
- if (s->verbose > 1 && s->commitable) {
+ if (s->verbose > 1 && s->committable) {
/* print_updated() printed a header, so do we */
if (s->fp != stdout)
wt_longstatus_print_trailer(s);
@@ -1063,22 +1090,8 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
strbuf_release(&sb);
}
-static int has_unmerged(struct wt_status *s)
-{
- int i;
-
- for (i = 0; i < s->change.nr; i++) {
- struct wt_status_change_data *d;
- d = s->change.items[i].util;
- if (d->stagemask)
- return 1;
- }
- return 0;
-}
-
static void show_merge_in_progress(struct wt_status *s,
- struct wt_status_state *state,
- const char *color)
+ const char *color)
{
if (has_unmerged(s)) {
status_printf_ln(s, color, _("You have unmerged paths."));
@@ -1089,7 +1102,6 @@ static void show_merge_in_progress(struct wt_status *s,
_(" (use \"git merge --abort\" to abort the merge)"));
}
} else {
- s-> commitable = 1;
status_printf_ln(s, color,
_("All conflicts fixed but you are still merging."));
if (s->hints)
@@ -1100,16 +1112,15 @@ static void show_merge_in_progress(struct wt_status *s,
}
static void show_am_in_progress(struct wt_status *s,
- struct wt_status_state *state,
const char *color)
{
status_printf_ln(s, color,
_("You are in the middle of an am session."));
- if (state->am_empty_patch)
+ if (s->state.am_empty_patch)
status_printf_ln(s, color,
_("The current patch is empty."));
if (s->hints) {
- if (!state->am_empty_patch)
+ if (!s->state.am_empty_patch)
status_printf_ln(s, color,
_(" (fix conflicts and then run \"git am --continue\")"));
status_printf_ln(s, color,
@@ -1233,10 +1244,9 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines)
}
static void show_rebase_information(struct wt_status *s,
- struct wt_status_state *state,
- const char *color)
+ const char *color)
{
- if (state->rebase_interactive_in_progress) {
+ if (s->state.rebase_interactive_in_progress) {
int i;
int nr_lines_to_show = 2;
@@ -1287,28 +1297,26 @@ static void show_rebase_information(struct wt_status *s,
}
static void print_rebase_state(struct wt_status *s,
- struct wt_status_state *state,
- const char *color)
+ const char *color)
{
- if (state->branch)
+ if (s->state.branch)
status_printf_ln(s, color,
_("You are currently rebasing branch '%s' on '%s'."),
- state->branch,
- state->onto);
+ s->state.branch,
+ s->state.onto);
else
status_printf_ln(s, color,
_("You are currently rebasing."));
}
static void show_rebase_in_progress(struct wt_status *s,
- struct wt_status_state *state,
- const char *color)
+ const char *color)
{
struct stat st;
- show_rebase_information(s, state, color);
+ show_rebase_information(s, color);
if (has_unmerged(s)) {
- print_rebase_state(s, state, color);
+ print_rebase_state(s, color);
if (s->hints) {
status_printf_ln(s, color,
_(" (fix conflicts and then run \"git rebase --continue\")"));
@@ -1317,17 +1325,18 @@ static void show_rebase_in_progress(struct wt_status *s,
status_printf_ln(s, color,
_(" (use \"git rebase --abort\" to check out the original branch)"));
}
- } else if (state->rebase_in_progress || !stat(git_path_merge_msg(the_repository), &st)) {
- print_rebase_state(s, state, color);
+ } else if (s->state.rebase_in_progress ||
+ !stat(git_path_merge_msg(the_repository), &st)) {
+ print_rebase_state(s, color);
if (s->hints)
status_printf_ln(s, color,
_(" (all conflicts fixed: run \"git rebase --continue\")"));
} else if (split_commit_in_progress(s)) {
- if (state->branch)
+ if (s->state.branch)
status_printf_ln(s, color,
_("You are currently splitting a commit while rebasing branch '%s' on '%s'."),
- state->branch,
- state->onto);
+ s->state.branch,
+ s->state.onto);
else
status_printf_ln(s, color,
_("You are currently splitting a commit during a rebase."));
@@ -1335,11 +1344,11 @@ static void show_rebase_in_progress(struct wt_status *s,
status_printf_ln(s, color,
_(" (Once your working directory is clean, run \"git rebase --continue\")"));
} else {
- if (state->branch)
+ if (s->state.branch)
status_printf_ln(s, color,
_("You are currently editing a commit while rebasing branch '%s' on '%s'."),
- state->branch,
- state->onto);
+ s->state.branch,
+ s->state.onto);
else
status_printf_ln(s, color,
_("You are currently editing a commit during a rebase."));
@@ -1354,11 +1363,10 @@ static void show_rebase_in_progress(struct wt_status *s,
}
static void show_cherry_pick_in_progress(struct wt_status *s,
- struct wt_status_state *state,
- const char *color)
+ const char *color)
{
status_printf_ln(s, color, _("You are currently cherry-picking commit %s."),
- find_unique_abbrev(&state->cherry_pick_head_oid, DEFAULT_ABBREV));
+ find_unique_abbrev(&s->state.cherry_pick_head_oid, DEFAULT_ABBREV));
if (s->hints) {
if (has_unmerged(s))
status_printf_ln(s, color,
@@ -1373,11 +1381,10 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
}
static void show_revert_in_progress(struct wt_status *s,
- struct wt_status_state *state,
- const char *color)
+ const char *color)
{
status_printf_ln(s, color, _("You are currently reverting commit %s."),
- find_unique_abbrev(&state->revert_head_oid, DEFAULT_ABBREV));
+ find_unique_abbrev(&s->state.revert_head_oid, DEFAULT_ABBREV));
if (s->hints) {
if (has_unmerged(s))
status_printf_ln(s, color,
@@ -1392,13 +1399,12 @@ static void show_revert_in_progress(struct wt_status *s,
}
static void show_bisect_in_progress(struct wt_status *s,
- struct wt_status_state *state,
- const char *color)
+ const char *color)
{
- if (state->branch)
+ if (s->state.branch)
status_printf_ln(s, color,
_("You are currently bisecting, started from branch '%s'."),
- state->branch);
+ s->state.branch);
else
status_printf_ln(s, color,
_("You are currently bisecting."));
@@ -1572,48 +1578,45 @@ void wt_status_get_state(struct wt_status_state *state,
wt_status_get_detached_from(state);
}
-static void wt_longstatus_print_state(struct wt_status *s,
- struct wt_status_state *state)
+static void wt_longstatus_print_state(struct wt_status *s)
{
const char *state_color = color(WT_STATUS_HEADER, s);
+ struct wt_status_state *state = &s->state;
+
if (state->merge_in_progress)
- show_merge_in_progress(s, state, state_color);
+ show_merge_in_progress(s, state_color);
else if (state->am_in_progress)
- show_am_in_progress(s, state, state_color);
+ show_am_in_progress(s, state_color);
else if (state->rebase_in_progress || state->rebase_interactive_in_progress)
- show_rebase_in_progress(s, state, state_color);
+ show_rebase_in_progress(s, state_color);
else if (state->cherry_pick_in_progress)
- show_cherry_pick_in_progress(s, state, state_color);
+ show_cherry_pick_in_progress(s, state_color);
else if (state->revert_in_progress)
- show_revert_in_progress(s, state, state_color);
+ show_revert_in_progress(s, state_color);
if (state->bisect_in_progress)
- show_bisect_in_progress(s, state, state_color);
+ show_bisect_in_progress(s, state_color);
}
static void wt_longstatus_print(struct wt_status *s)
{
const char *branch_color = color(WT_STATUS_ONBRANCH, s);
const char *branch_status_color = color(WT_STATUS_HEADER, s);
- struct wt_status_state state;
-
- memset(&state, 0, sizeof(state));
- wt_status_get_state(&state,
- s->branch && !strcmp(s->branch, "HEAD"));
if (s->branch) {
const char *on_what = _("On branch ");
const char *branch_name = s->branch;
if (!strcmp(branch_name, "HEAD")) {
branch_status_color = color(WT_STATUS_NOBRANCH, s);
- if (state.rebase_in_progress || state.rebase_interactive_in_progress) {
- if (state.rebase_interactive_in_progress)
+ if (s->state.rebase_in_progress ||
+ s->state.rebase_interactive_in_progress) {
+ if (s->state.rebase_interactive_in_progress)
on_what = _("interactive rebase in progress; onto ");
else
on_what = _("rebase in progress; onto ");
- branch_name = state.onto;
- } else if (state.detached_from) {
- branch_name = state.detached_from;
- if (state.detached_at)
+ branch_name = s->state.onto;
+ } else if (s->state.detached_from) {
+ branch_name = s->state.detached_from;
+ if (s->state.detached_at)
on_what = _("HEAD detached at ");
else
on_what = _("HEAD detached from ");
@@ -1630,10 +1633,7 @@ static void wt_longstatus_print(struct wt_status *s)
wt_longstatus_print_tracking(s);
}
- wt_longstatus_print_state(s, &state);
- free(state.branch);
- free(state.onto);
- free(state.detached_from);
+ wt_longstatus_print_state(s);
if (s->is_initial) {
status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
@@ -1665,14 +1665,14 @@ static void wt_longstatus_print(struct wt_status *s)
"new files yourself (see 'git help status')."),
s->untracked_in_ms / 1000.0);
}
- } else if (s->commitable)
+ } else if (s->committable)
status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"),
s->hints
? _(" (use -u option to show untracked files)") : "");
if (s->verbose)
wt_longstatus_print_verbose(s);
- if (!s->commitable) {
+ if (!s->committable) {
if (s->amend)
status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
else if (s->nowarn)
@@ -1937,13 +1937,9 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
struct branch *branch;
const char *base;
const char *branch_name;
- struct wt_status_state state;
int ab_info, nr_ahead, nr_behind;
char eol = s->null_termination ? '\0' : '\n';
- memset(&state, 0, sizeof(state));
- wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD"));
-
fprintf(s->fp, "# branch.oid %s%c",
(s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)),
eol);
@@ -1954,10 +1950,11 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
if (!strcmp(s->branch, "HEAD")) {
fprintf(s->fp, "# branch.head %s%c", "(detached)", eol);
- if (state.rebase_in_progress || state.rebase_interactive_in_progress)
- branch_name = state.onto;
- else if (state.detached_from)
- branch_name = state.detached_from;
+ if (s->state.rebase_in_progress ||
+ s->state.rebase_interactive_in_progress)
+ branch_name = s->state.onto;
+ else if (s->state.detached_from)
+ branch_name = s->state.detached_from;
else
branch_name = "";
} else {
@@ -1991,10 +1988,6 @@ static void wt_porcelain_v2_print_tracking(struct wt_status *s)
}
}
}
-
- free(state.branch);
- free(state.onto);
- free(state.detached_from);
}
/*
@@ -2314,7 +2307,7 @@ int has_unstaged_changes(int ignore_submodules)
struct rev_info rev_info;
int result;
- init_revisions(&rev_info, NULL);
+ repo_init_revisions(the_repository, &rev_info, NULL);
if (ignore_submodules) {
rev_info.diffopt.flags.ignore_submodules = 1;
rev_info.diffopt.flags.override_submodule_config = 1;
@@ -2336,7 +2329,7 @@ int has_uncommitted_changes(int ignore_submodules)
if (is_cache_unborn())
return 0;
- init_revisions(&rev_info, NULL);
+ repo_init_revisions(the_repository, &rev_info, NULL);
if (ignore_submodules)
rev_info.diffopt.flags.ignore_submodules = 1;
rev_info.diffopt.flags.quick = 1;
diff --git a/wt-status.h b/wt-status.h
index 1673d146fa..1fcf93afbf 100644
--- a/wt-status.h
+++ b/wt-status.h
@@ -64,6 +64,24 @@ enum wt_status_format {
STATUS_FORMAT_UNSPECIFIED
};
+struct wt_status_state {
+ int merge_in_progress;
+ int am_in_progress;
+ int am_empty_patch;
+ int rebase_in_progress;
+ int rebase_interactive_in_progress;
+ int cherry_pick_in_progress;
+ int bisect_in_progress;
+ int revert_in_progress;
+ int detached_at;
+ char *branch;
+ char *onto;
+ char *detached_from;
+ struct object_id detached_oid;
+ struct object_id revert_head_oid;
+ struct object_id cherry_pick_head_oid;
+};
+
struct wt_status {
int is_initial;
char *branch;
@@ -93,10 +111,11 @@ struct wt_status {
int rename_score;
int rename_limit;
enum wt_status_format status_format;
+ struct wt_status_state state;
unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */
/* These are computed during processing of the individual sections */
- int commitable;
+ int committable;
int workdir_dirty;
const char *index_file;
FILE *fp;
@@ -107,29 +126,12 @@ struct wt_status {
uint32_t untracked_in_ms;
};
-struct wt_status_state {
- int merge_in_progress;
- int am_in_progress;
- int am_empty_patch;
- int rebase_in_progress;
- int rebase_interactive_in_progress;
- int cherry_pick_in_progress;
- int bisect_in_progress;
- int revert_in_progress;
- int detached_at;
- char *branch;
- char *onto;
- char *detached_from;
- struct object_id detached_oid;
- struct object_id revert_head_oid;
- struct object_id cherry_pick_head_oid;
-};
-
size_t wt_status_locate_end(const char *s, size_t len);
void wt_status_add_cut_line(FILE *fp);
void wt_status_prepare(struct wt_status *s);
void wt_status_print(struct wt_status *s);
void wt_status_collect(struct wt_status *s);
+void wt_status_collect_free_buffers(struct wt_status *s);
void wt_status_get_state(struct wt_status_state *state, int get_detached_from);
int wt_status_check_rebase(const struct worktree *wt,
struct wt_status_state *state);