summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/RelNotes/2.14.0.txt306
-rw-r--r--Documentation/config.txt15
-rw-r--r--Documentation/git-clone.txt14
-rw-r--r--Documentation/git-grep.txt7
-rw-r--r--Documentation/git-pull.txt6
-rw-r--r--Documentation/git-repack.txt5
-rw-r--r--Documentation/git-send-email.txt1
-rw-r--r--Documentation/githooks.txt8
-rw-r--r--Documentation/rev-list-options.txt8
-rw-r--r--Documentation/technical/api-directory-listing.txt6
-rw-r--r--Documentation/technical/api-parse-options.txt8
-rw-r--r--Documentation/technical/api-sub-process.txt59
-rwxr-xr-xGIT-VERSION-GEN2
-rw-r--r--Makefile16
l---------RelNotes2
-rw-r--r--archive-tar.c7
-rw-r--r--archive-zip.c227
-rw-r--r--archive.c6
-rw-r--r--archive.h2
-rw-r--r--bisect.c2
-rw-r--r--blame.c1863
-rw-r--r--blame.h175
-rw-r--r--blob.c6
-rw-r--r--blob.h2
-rw-r--r--branch.c31
-rw-r--r--builtin.h2
-rw-r--r--builtin/add.c19
-rw-r--r--builtin/am.c22
-rw-r--r--builtin/blame.c2126
-rw-r--r--builtin/branch.c6
-rw-r--r--builtin/cat-file.c5
-rw-r--r--builtin/check-ignore.c9
-rw-r--r--builtin/checkout.c18
-rw-r--r--builtin/clean.c46
-rw-r--r--builtin/clone.c17
-rw-r--r--builtin/commit-tree.c2
-rw-r--r--builtin/commit.c12
-rw-r--r--builtin/config.c3
-rw-r--r--builtin/describe.c10
-rw-r--r--builtin/diff-files.c2
-rw-r--r--builtin/diff-index.c2
-rw-r--r--builtin/diff-tree.c10
-rw-r--r--builtin/diff.c64
-rw-r--r--builtin/fast-export.c8
-rw-r--r--builtin/fetch.c9
-rw-r--r--builtin/fmt-merge-msg.c8
-rw-r--r--builtin/fsck.c22
-rw-r--r--builtin/gc.c2
-rw-r--r--builtin/grep.c31
-rw-r--r--builtin/index-pack.c56
-rw-r--r--builtin/log.c24
-rw-r--r--builtin/ls-files.c41
-rw-r--r--builtin/ls-tree.c6
-rw-r--r--builtin/merge-base.c8
-rw-r--r--builtin/merge-tree.c10
-rw-r--r--builtin/merge.c12
-rw-r--r--builtin/name-rev.c99
-rw-r--r--builtin/notes.c2
-rw-r--r--builtin/pack-objects.c79
-rw-r--r--builtin/prune.c11
-rw-r--r--builtin/pull.c35
-rw-r--r--builtin/read-tree.c10
-rw-r--r--builtin/receive-pack.c22
-rw-r--r--builtin/reflog.c60
-rw-r--r--builtin/remote.c4
-rw-r--r--builtin/repack.c5
-rw-r--r--builtin/replace.c6
-rw-r--r--builtin/reset.c41
-rw-r--r--builtin/rev-list.c4
-rw-r--r--builtin/rev-parse.c58
-rw-r--r--builtin/rm.c3
-rw-r--r--builtin/show-branch.c8
-rw-r--r--builtin/submodule--helper.c3
-rw-r--r--builtin/tag.c66
-rw-r--r--builtin/unpack-objects.c65
-rw-r--r--builtin/verify-commit.c12
-rw-r--r--builtin/worktree.c4
-rw-r--r--bulk-checkin.c4
-rw-r--r--bundle.c43
-rw-r--r--bundle.h4
-rw-r--r--cache-tree.c37
-rw-r--r--cache-tree.h3
-rw-r--r--cache.h29
-rwxr-xr-xci/run-windows-build.sh26
-rw-r--r--commit.c56
-rw-r--r--commit.h18
-rw-r--r--compat/mingw.c93
-rw-r--r--config.c18
-rw-r--r--configure.ac12
-rw-r--r--connect.c2
-rw-r--r--contrib/completion/git-completion.bash6
-rw-r--r--convert.c161
-rw-r--r--credential-cache--daemon.c12
-rw-r--r--date.c113
-rw-r--r--diff-lib.c12
-rw-r--r--diff.c35
-rw-r--r--diff.h9
-rw-r--r--dir.c243
-rw-r--r--dir.h33
-rw-r--r--entry.c8
-rw-r--r--environment.c4
-rw-r--r--fast-import.c447
-rw-r--r--fetch-pack.c145
-rw-r--r--fsck.c8
-rwxr-xr-xgit-add--interactive.perl4
-rw-r--r--git-compat-util.h17
-rwxr-xr-xgit-send-email.perl76
-rw-r--r--grep.c110
-rw-r--r--grep.h10
-rw-r--r--help.c43
-rw-r--r--http-backend.c6
-rw-r--r--http-push.c55
-rw-r--r--list-objects.c4
-rw-r--r--lockfile.h8
-rw-r--r--log-tree.c26
-rw-r--r--merge-recursive.c16
-rw-r--r--merge.c4
-rw-r--r--notes-cache.c29
-rw-r--r--notes-cache.h4
-rw-r--r--notes-merge.c26
-rw-r--r--notes-utils.c18
-rw-r--r--object.c38
-rw-r--r--object.h8
-rw-r--r--pack-bitmap-write.c8
-rw-r--r--pack-bitmap.c4
-rw-r--r--pack-check.c25
-rw-r--r--pack-objects.c8
-rw-r--r--pack-write.c10
-rw-r--r--pack.h4
-rw-r--r--parse-options-cb.c10
-rw-r--r--pathspec.c86
-rw-r--r--pathspec.h25
-rw-r--r--pkt-line.c33
-rw-r--r--pkt-line.h13
-rw-r--r--pretty.c6
-rw-r--r--reachable.c17
-rw-r--r--reachable.h4
-rw-r--r--read-cache.c34
-rw-r--r--ref-filter.c124
-rw-r--r--ref-filter.h2
-rw-r--r--reflog-walk.c14
-rw-r--r--refs.c253
-rw-r--r--refs.h175
-rw-r--r--refs/files-backend.c504
-rw-r--r--refs/iterator.c18
-rw-r--r--refs/ref-cache.c103
-rw-r--r--refs/ref-cache.h6
-rw-r--r--refs/refs-internal.h70
-rw-r--r--remote.c45
-rw-r--r--revision.c312
-rw-r--r--revision.h10
-rw-r--r--run-command.c457
-rw-r--r--run-command.h1
-rw-r--r--sequencer.c59
-rw-r--r--server-info.c2
-rw-r--r--sha1_name.c55
-rw-r--r--shallow.c34
-rw-r--r--split-index.c57
-rw-r--r--split-index.h1
-rw-r--r--string-list.c18
-rw-r--r--string-list.h7
-rw-r--r--sub-process.c106
-rw-r--r--sub-process.h49
-rw-r--r--submodule.c447
-rw-r--r--submodule.h12
-rw-r--r--t/README8
-rw-r--r--t/helper/test-date.c18
-rw-r--r--t/helper/test-dump-cache-tree.c4
-rw-r--r--t/helper/test-match-trees.c4
-rw-r--r--t/helper/test-parse-options.c4
-rw-r--r--t/helper/test-ref-store.c25
-rwxr-xr-xt/lib-submodule-update.sh31
-rw-r--r--t/perf/README18
-rwxr-xr-xt/perf/p0004-lazy-init-name-hash.sh47
-rwxr-xr-xt/perf/p0100-globbing.sh43
-rwxr-xr-xt/perf/p3400-rebase.sh22
-rwxr-xr-xt/perf/p4220-log-grep-engines.sh53
-rwxr-xr-xt/perf/p4221-log-grep-engines-fixed.sh44
-rwxr-xr-xt/perf/p7820-grep-engines.sh56
-rwxr-xr-xt/perf/p7821-grep-engines-fixed.sh41
-rw-r--r--t/perf/perf-lib.sh19
-rwxr-xr-xt/perf/run13
-rwxr-xr-xt/t0006-date.sh4
-rwxr-xr-xt/t0061-run-command.sh41
-rwxr-xr-xt/t1013-read-tree-submodule.sh1
-rwxr-xr-xt/t1300-repo-config.sh6
-rwxr-xr-xt/t1305-config-include.sh23
-rwxr-xr-xt/t1405-main-ref-store.sh2
-rwxr-xr-xt/t1406-submodule-ref-store.sh2
-rwxr-xr-xt/t1407-worktree-ref-store.sh52
-rwxr-xr-xt/t1601-index-bogus.sh22
-rwxr-xr-xt/t2013-checkout-submodule.sh1
-rwxr-xr-xt/t3070-wildmatch.sh1
-rwxr-xr-xt/t3600-rm.sh4
-rwxr-xr-xt/t4051-diff-function-context.sh3
-rwxr-xr-xt/t4060-diff-submodule-option-diff-format.sh41
-rwxr-xr-xt/t4061-diff-indent.sh184
-rwxr-xr-xt/t4063-diff-blobs.sh96
-rwxr-xr-xt/t4202-log.sh171
-rwxr-xr-xt/t5000-tar-tree.sh6
-rwxr-xr-xt/t5004-archive-corner-cases.sh50
-rw-r--r--t/t5004/big-pack.zipbin0 -> 7373 bytes
-rwxr-xr-xt/t5300-pack-object.sh36
-rwxr-xr-xt/t5500-fetch-pack.sh35
-rwxr-xr-xt/t5520-pull.sh18
-rwxr-xr-xt/t5545-push-options.sh85
-rwxr-xr-xt/t5550-http-fetch-dumb.sh5
-rwxr-xr-xt/t5612-clone-refspec.sh103
-rwxr-xr-xt/t5614-clone-submodules-shallow.sh (renamed from t/t5614-clone-submodules.sh)0
-rwxr-xr-xt/t6134-pathspec-in-submodule.sh6
-rwxr-xr-xt/t7008-grep-binary.sh135
-rwxr-xr-xt/t7009-filter-branch-null-sha1.sh6
-rwxr-xr-xt/t7061-wtstatus-ignore.sh1
-rwxr-xr-xt/t7063-status-untracked-cache.sh22
-rwxr-xr-xt/t7112-reset-submodule.sh8
-rwxr-xr-xt/t7300-clean.sh16
-rwxr-xr-xt/t7508-status.sh119
-rwxr-xr-xt/t7810-grep.sh81
-rwxr-xr-xt/t7812-grep-icase-non-ascii.sh29
-rwxr-xr-xt/t7813-grep-icase-iso.sh2
-rwxr-xr-xt/t7814-grep-recurse-submodules.sh215
-rwxr-xr-xt/t9001-send-email.sh48
-rw-r--r--t/test-lib.sh39
-rw-r--r--tag.c31
-rw-r--r--tag.h4
-rw-r--r--transport.c2
-rw-r--r--tree-walk.c1
-rw-r--r--tree.c22
-rw-r--r--tree.h4
-rw-r--r--unpack-trees.c18
-rw-r--r--upload-pack.c68
-rw-r--r--usage.c42
-rw-r--r--vcs-svn/fast_export.c8
-rw-r--r--vcs-svn/fast_export.h4
-rw-r--r--vcs-svn/svndump.c2
-rw-r--r--walker.c8
-rw-r--r--worktree.c102
-rw-r--r--worktree.h2
-rw-r--r--wt-status.c8
239 files changed, 8604 insertions, 5195 deletions
diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt
new file mode 100644
index 0000000000..e4ca72e50e
--- /dev/null
+++ b/Documentation/RelNotes/2.14.0.txt
@@ -0,0 +1,306 @@
+Git 2.14 Release Notes
+======================
+
+Backward compatibility notes.
+
+ * Use of an empty string as a pathspec element that is used for
+ 'everything matches' is still warned and Git asks users to use a
+ more explicit '.' for that instead. The hope is that existing
+ users will not mind this change, and eventually the warning can be
+ turned into a hard error, upgrading the deprecation into removal of
+ this (mis)feature. That is not scheduled to happen in the upcoming
+ release (yet).
+
+ * Git now avoids blindly falling back to ".git" when the setup
+ sequence said we are _not_ in Git repository. A corner case that
+ happens to work right now may be broken by a call to die("BUG").
+ We've tried hard to locate such cases and fixed them, but there
+ might still be cases that need to be addressed--bug reports are
+ greatly appreciated.
+
+ * The experiment to improve the hunk-boundary selection of textual
+ diff output has finished, and the "indent heuristics" has now
+ become the default.
+
+
+Updates since v2.13
+-------------------
+
+UI, Workflows & Features
+
+ * The colors in which "git status --short --branch" showed the names
+ of the current branch and its remote-tracking branch are now
+ configurable.
+
+ * "git clone" learned the "--no-tags" option not to fetch all tags
+ initially, and also set up the tagopt not to follow any tags in
+ subsequent fetches.
+
+ * "git archive --format=zip" learned to use zip64 extension when
+ necessary to go beyond the 4GB limit.
+ (merge 867e40ff3a rs/large-zip later to maint).
+
+ * "git reset" learned "--recurse-submodules" option.
+
+ * "git diff --submodule=diff" now recurses into nested submodules.
+ (merge 5a5221427c jk/diff-submodule-diff-inline later to maint).
+
+ * "git repack" learned to accept the --threads=<n> option and pass it
+ to pack-objects.
+
+ * "git send-email" learned to run sendemail-validate hook to inspect
+ and reject a message before sending it out.
+ (merge 177409e589 jt/send-email-validate-hook later to maint).
+
+ * There is no good reason why "git fetch $there $sha1" should fail
+ when the $sha1 names an object at the tip of an advertised ref,
+ even when the other side hasn't enabled allowTipSHA1InWant.
+
+ * The recently introduced "[includeIf "gitdir:$dir"] path=..."
+ mechansim has further been taught to take symlinks into account.
+ The directory "$dir" specified in "gitdir:$dir" may be a symlink to
+ a real location, not something that $(getcwd) may return. In such
+ a case, a realpath of "$dir" is compared with the real path of the
+ current repository to determine if the contents from the named path
+ should be included.
+
+ * Make the "indent" heuristics the default in "diff" and diff.indentHeuristics
+ configuration variable an escape hatch for those who do no want it.
+
+
+Performance, Internal Implementation, Development Support etc.
+
+ * The default packed-git limit value has been raised on larger
+ platforms to save "git fetch" from a (recoverable) failure while
+ "gc" is running in parallel.
+
+ * Code to update the cache-tree has been tightened so that we won't
+ accidentally write out any 0{40} entry in the tree object.
+ (merge a96d3cc3f6 jk/no-null-sha1-in-cache-tree later to maint).
+
+ * Attempt to allow us notice "fishy" situation where we fail to
+ remove the temporary directory used during the test.
+
+ * Travis CI gained a task to format the documentation with both
+ AsciiDoc and AsciiDoctor.
+
+ * Some platforms have ulong that is smaller than time_t, and our
+ historical use of ulong for timestamp would mean they cannot
+ represent some timestamp that the platform allows. Invent a
+ separate and dedicated timestamp_t (so that we can distingiuish
+ timestamps and a vanilla ulongs, which along is already a good
+ move), and then declare uintmax_t is the type to be used as the
+ timestamp_t.
+
+ * We can trigger Windows auto-build tester (credits: Dscho &
+ Microsoft) from our existing Travis CI tester now.
+
+ * Conversion from uchar[20] to struct object_id continues.
+
+ * Simplify parse_pathspec() codepath and stop it from looking at the
+ default in-core index.
+ (merge 08de9151a8 bw/pathspec-sans-the-index later to maint).
+
+ * Add perf-test for wildmatch.
+ (merge 62ca75a6b9 ab/perf-wildmatch later to maint).
+
+ * Code from "conversion using external process" codepath has been
+ extracted to a separate sub-process.[ch] module.
+ (merge 4f2a2e9f0e bp/sub-process-convert-filter later to maint).
+
+ * When "git checkout", "git merge", etc. manipulates the in-core
+ index, various pieces of information in the index extensions are
+ discarded from the original state, as it is usually not the case
+ that they are kept up-to-date and in-sync with the operation on the
+ main index. The untracked cache extension is copied across these
+ operations now, which would speed up "git status" (as long as the
+ cache is properly invalidated).
+
+ * The internal implementation of "git grep" has seen some clean-up.
+ (merge 8df4c2953f ab/grep-preparatory-cleanup later to maint).
+
+ * Update the C style recommendation for notes for translators, as
+ recent versions of gettext tools can work with our style of
+ multi-line comments.
+
+ * The implementation of "ref" API around the "packed refs" have been
+ cleaned up, in preparation for further changes.
+
+ * The internal logic used in "git blame" has been libified to make it
+ easier to use by cgit.
+
+
+Also contains various documentation updates and code clean-ups.
+
+
+Fixes since v2.13
+-----------------
+
+Unless otherwise noted, all the fixes since v2.13 in the maintenance
+track are contained in this release (see the maintenance releases'
+notes for details).
+
+ * "git gc" did not interact well with "git worktree"-managed
+ per-worktree refs.
+
+ * "git cherry-pick" and other uses of the sequencer machinery
+ mishandled a trailer block whose last line is an incomplete line.
+ This has been fixed so that an additional sign-off etc. are added
+ after completing the existing incomplete line.
+
+ * The codepath in "git am" that is used when running "git rebase"
+ leaked memory held for the log message of the commits being rebased.
+
+ * "git clone --config var=val" is a way to populate the
+ per-repository configuration file of the new repository, but it did
+ not work well when val is an empty string. This has been fixed.
+
+ * Setting "log.decorate=false" in the configuration file did not take
+ effect in v2.13, which has been corrected.
+
+ * A few codepaths in "checkout" and "am" working on an unborn branch
+ tried to access an uninitialized piece of memory.
+
+ * The Web interface to gmane news archive is long gone, even though
+ the articles are still accessible via NTTP. Replace the links with
+ ones to public-inbox.org. Because their message identification is
+ based on the actual message-id, it is likely that it will be easier
+ to migrate away from it if/when necessary.
+
+ * The receive-pack program now makes sure that the push certificate
+ records the same set of push options used for pushing.
+
+ * Tests have been updated to pass under GETTEXT_POISON (a mechanism
+ to ensure that output strings that should not be translated are
+ not translated by mistake), and TravisCI is told to run them.
+
+ * "git checkout --recurse-submodules" did not quite work with a
+ submodule that itself has submodules.
+ (merge 218c883783 sb/checkout-recurse-submodules later to maint).
+
+ * Plug some leaks and updates internal API used to implement the
+ split index feature to make it easier to avoid such a leak in the
+ future.
+ (merge de950c5773 nd/split-index-unshare later to maint).
+
+ * "pack-objects" can stream a slice of an existing packfile out when
+ the pack bitmap can tell that the reachable objects are all needed
+ in the output, without inspecting individual objects. This
+ strategy however would not work well when "--local" and other
+ options are in use, and need to be disabled.
+
+ * Fix memory leaks pointed out by Coverity (and people).
+
+ * "git read-tree -m" (no tree-ish) gave a nonsense suggestion "use
+ --empty if you want to clear the index". With "-m", such a request
+ will still fail anyway, as you'd need to name at least one tree-ish
+ to be merged.
+
+ * Make sure our tests would pass when the sources are checked out
+ with "platform native" line ending convention by default on
+ Windows. Some "text" files out tests use and the test scripts
+ themselves that are meant to be run with /bin/sh, ought to be
+ checked out with eol=LF even on Windows.
+
+ * Introduce the BUG() macro to improve die("BUG: ...").
+ (merge 3d7dd2d3b6 jk/bug-to-abort later to maint).
+
+ * Clarify documentation for include.path and includeIf.<condition>.path
+ configuration variables.
+
+ * Git sometimes gives an advice in a rhetorical question that does
+ not require an answer, which can confuse new users and non native
+ speakers. Attempt to rephrase them.
+
+ * A few http:// links that are redirected to https:// in the
+ documentation have been updated to https:// links.
+
+ * "git for-each-ref --format=..." with %(HEAD) in the format used to
+ resolve the HEAD symref as many times as it had processed refs,
+ which was wasteful, and "git branch" shared the same problem.
+
+ * Regression fix to topic recently merged to 'master'.
+
+ * The shell completion script (in contrib/) learned "git stash" has
+ a new "push" subcommand.
+
+ * "git interpret-trailers", when used as GIT_EDITOR for "git commit
+ -v", looked for and appended to a trailer block at the very end,
+ i.e. at the end of the "diff" output. The command has been
+ corrected to pay attention to the cut-mark line "commit -v" adds to
+ the buffer---the real trailer block should appear just before it.
+
+ * A test allowed both "git push" and "git receive-pack" on the other
+ end write their traces into the same file. This is OK on platforms
+ that allows atomically appending to a file opened with O_APPEND,
+ but on other platforms led to a mangled output, causing
+ intermittent test failures. This has been fixed by disabling
+ traces from "receive-pack" in the test.
+
+ * Tag objects, which are not reachable from any ref, that point at
+ missing objects were mishandled by "git gc" and friends (they
+ should silently be ignored instead)
+
+ * "git describe --contains" penalized light-weight tags so much that
+ they were almost never considered. Instead, give them about the
+ same chance to be considered as an annotated tag that is the same
+ age as the underlying commit would.
+ (merge ef1e74065c jc/name-rev-lw-tag later to maint).
+
+ * The "run-command" API implementation has been made more robust
+ against dead-locking in a threaded environment.
+ (merge e3f43ce765 bw/forking-and-threading later to maint).
+
+ * A recent update to t5545-push-options.sh started skipping all the
+ tests in the script when a web server testing is disabled or
+ unavailable, not just the ones that require a web server. Non HTTP
+ tests have been salvaged to always run in this script.
+ (merge 2e397e4ddf jc/skip-test-in-the-middle later to maint).
+
+ * "git send-email" now uses Net::SMTP::SSL, which is obsolete, only
+ when needed. Recent versions of Net::SMTP can do TLS natively.
+ (merge bfbfc9a953 dk/send-email-avoid-net-smtp-ssl-when-able later to maint).
+
+ * "foo\bar\baz" in "git fetch foo\bar\baz", even though there is no
+ slashes in it, cannot be a nickname for a remote on Windows, as
+ that is likely to be a pathname on a local filesystem.
+
+ * "git clean -d" used to clean directories that has ignored files,
+ even though the command should not lose ignored ones without "-x".
+ "git status --ignored" did not list ignored and untracked files
+ without "-uall". These have been corrected.
+ (merge 6b1db43109 sl/clean-d-ignored-fix later to maint).
+
+ * The result from "git diff" that compares two blobs, e.g. "git diff
+ $commit1:$path $commit2:$path", used to be shown with the full
+ object name as given on the command line, but it is more natural to
+ use the $path in the output and use it to look up .gitattributes.
+ (merge 30d005c020 jk/diff-blob later to maint).
+
+ * The "collision detecting" SHA-1 implementation shipped with 2.13
+ was quite broken on some big-endian platforms and/or platforms that
+ do not like unaligned fetches. Update to the upstream code which
+ has already fixed these issues.
+
+ * "git am -h" triggered a BUG().
+
+ * The interaction of "url.*.insteadOf" and custom URL scheme's
+ whitelisting is now documented better.
+
+ * The timestamp of the index file is now taken after the file is
+ closed, to help Windows, on which a stale timestamp is reported by
+ fstat() on a file that is opened for writing and data was written
+ but not yet closed.
+ (merge 9f41c7a6b3 jh/close-index-before-stat later to maint).
+
+ * "git pull --rebase --autostash" didn't auto-stash when the local history
+ fast-forwards to the upstream.
+ (merge f15e7cf5cc tb/pull-ff-rebase-autostash later to maint).
+
+ * Other minor doc, test and build updates and code cleanups.
+ (merge c5a9157393 jh/memihash-opt later to maint).
+ (merge 44e2ff09ce ab/t3070-test-dedup later to maint).
+ (merge 9ee4aa95db rf/completion-config-commit later to maint).
+ (merge ef4fe5617e jk/connect-symref-info-leak-fix later to maint).
+ (merge a56eea28c4 jk/drop-free-refspecs later to maint).
+ (merge 0c79cee697 ad/pull-remote-doc later to maint).
diff --git a/Documentation/config.txt b/Documentation/config.txt
index a6a589a735..dd4beec39d 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -145,6 +145,16 @@ A few more notes on matching via `gitdir` and `gitdir/i`:
* Symlinks in `$GIT_DIR` are not resolved before matching.
+ * Both the symlink & realpath versions of paths will be matched
+ outside of `$GIT_DIR`. E.g. if ~/git is a symlink to
+ /mnt/storage/git, both `gitdir:~/git` and `gitdir:/mnt/storage/git`
+ will match.
++
+This was not the case in the initial release of this feature in
+v2.13.0, which only matched the realpath version. Configuration that
+wants to be compatible with the initial release of this feature needs
+to either specify only the realpath version, or both versions.
+
* Note that "../" is not special and will match literally, which is
unlikely what you want.
@@ -1149,7 +1159,10 @@ color.status.<slot>::
`untracked` (files which are not tracked by Git),
`branch` (the current branch),
`nobranch` (the color the 'no branch' warning is shown in, defaulting
- to red), or
+ to red),
+ `localBranch` or `remoteBranch` (the local and remote branch names,
+ respectively, when branch and tracking information is displayed in the
+ status short-format), or
`unmerged` (files which have unmerged changes).
color.ui::
diff --git a/Documentation/git-clone.txt b/Documentation/git-clone.txt
index 30052cce49..83c8e9b394 100644
--- a/Documentation/git-clone.txt
+++ b/Documentation/git-clone.txt
@@ -13,7 +13,7 @@ SYNOPSIS
[-l] [-s] [--no-hardlinks] [-q] [-n] [--bare] [--mirror]
[-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
[--dissociate] [--separate-git-dir <git dir>]
- [--depth <depth>] [--[no-]single-branch]
+ [--depth <depth>] [--[no-]single-branch] [--no-tags]
[--recurse-submodules] [--[no-]shallow-submodules]
[--jobs <n>] [--] <repository> [<directory>]
@@ -215,6 +215,18 @@ objects from the source repository into a pack in the cloned repository.
branch when `--single-branch` clone was made, no remote-tracking
branch is created.
+--no-tags::
+ Don't clone any tags, and set
+ `remote.<remote>.tagOpt=--no-tags` in the config, ensuring
+ that future `git pull` and `git fetch` operations won't follow
+ any tags. Subsequent explicit tag fetches will still work,
+ (see linkgit:git-fetch[1]).
++
+Can be used in conjunction with `--single-branch` to clone and
+maintain a branch with no references other than a single cloned
+branch. This is useful e.g. to maintain minimal clones of the default
+branch of some repository for search indexing.
+
--recurse-submodules[=<pathspec]::
After the clone is created, initialize and clone submodules
within based on the provided pathspec. If no pathspec is
diff --git a/Documentation/git-grep.txt b/Documentation/git-grep.txt
index 71f32f3508..5033483db4 100644
--- a/Documentation/git-grep.txt
+++ b/Documentation/git-grep.txt
@@ -161,8 +161,11 @@ OPTIONS
-P::
--perl-regexp::
- Use Perl-compatible regexp for patterns. Requires libpcre to be
- compiled in.
+ Use Perl-compatible regular expressions for patterns.
++
+Support for these types of regular expressions is an optional
+compile-time dependency. If Git wasn't compiled with support for them
+providing this option will cause it to die.
-F::
--fixed-strings::
diff --git a/Documentation/git-pull.txt b/Documentation/git-pull.txt
index 942af8e0f7..e414185f5a 100644
--- a/Documentation/git-pull.txt
+++ b/Documentation/git-pull.txt
@@ -159,15 +159,15 @@ present while on branch `<name>`, that value is used instead of
In order to determine what URL to use to fetch from, the value
of the configuration `remote.<origin>.url` is consulted
-and if there is not any such variable, the value on `URL: ` line
-in `$GIT_DIR/remotes/<origin>` file is used.
+and if there is not any such variable, the value on the `URL:` line
+in `$GIT_DIR/remotes/<origin>` is used.
In order to determine what remote branches to fetch (and
optionally store in the remote-tracking branches) when the command is
run without any refspec parameters on the command line, values
of the configuration variable `remote.<origin>.fetch` are
consulted, and if there aren't any, `$GIT_DIR/remotes/<origin>`
-file is consulted and its `Pull: ` lines are used.
+is consulted and its `Pull:` lines are used.
In addition to the refspec formats described in the OPTIONS
section, you can have a globbing refspec that looks like this:
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index 26afe6ed54..ae750e9e11 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository
SYNOPSIS
--------
[verse]
-'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>]
+'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>]
DESCRIPTION
-----------
@@ -92,6 +92,9 @@ other objects in that pack they already have locally.
to be applied that many times to get to the necessary object.
The default value for --window is 10 and --depth is 50.
+--threads=<n>::
+ This option is passed through to `git pack-objects`.
+
--window-memory=<n>::
This option provides an additional limit on top of `--window`;
the window size will dynamically scale down so as to not take
diff --git a/Documentation/git-send-email.txt b/Documentation/git-send-email.txt
index 9d66166f69..bb23b02caf 100644
--- a/Documentation/git-send-email.txt
+++ b/Documentation/git-send-email.txt
@@ -377,6 +377,7 @@ have been specified, in which case default to 'compose'.
Currently, validation means the following:
+
--
+ * Invoke the sendemail-validate hook if present (see linkgit:githooks[5]).
* Warn of patches that contain lines longer than 998 characters; this
is due to SMTP limits as described by http://www.ietf.org/rfc/rfc2821.txt.
--
diff --git a/Documentation/githooks.txt b/Documentation/githooks.txt
index 706091a569..b2514f4d44 100644
--- a/Documentation/githooks.txt
+++ b/Documentation/githooks.txt
@@ -447,6 +447,14 @@ rebase::
The commits are guaranteed to be listed in the order that they were
processed by rebase.
+sendemail-validate
+~~~~~~~~~~~~~~~~~~
+
+This hook is invoked by 'git send-email'. It takes a single parameter,
+the name of the file that holds the e-mail to be sent. Exiting with a
+non-zero status causes 'git send-email' to abort before sending any
+e-mails.
+
GIT
---
diff --git a/Documentation/rev-list-options.txt b/Documentation/rev-list-options.txt
index a02f7324c0..a46f70c2b1 100644
--- a/Documentation/rev-list-options.txt
+++ b/Documentation/rev-list-options.txt
@@ -92,8 +92,12 @@ endif::git-rev-list[]
pattern as a regular expression).
--perl-regexp::
- Consider the limiting patterns to be Perl-compatible regular expressions.
- Requires libpcre to be compiled in.
+ Consider the limiting patterns to be Perl-compatible regular
+ expressions.
++
+Support for these types of regular expressions is an optional
+compile-time dependency. If Git wasn't compiled with support for them
+providing this option will cause it to die.
--remove-empty::
Stop when a given path disappears from the tree.
diff --git a/Documentation/technical/api-directory-listing.txt b/Documentation/technical/api-directory-listing.txt
index 7f8e78d916..6c77b4920c 100644
--- a/Documentation/technical/api-directory-listing.txt
+++ b/Documentation/technical/api-directory-listing.txt
@@ -33,6 +33,12 @@ The notable options are:
Similar to `DIR_SHOW_IGNORED`, but return ignored files in `ignored[]`
in addition to untracked files in `entries[]`.
+`DIR_KEEP_UNTRACKED_CONTENTS`:::
+
+ Only has meaning if `DIR_SHOW_IGNORED_TOO` is also set; if this is set, the
+ untracked contents of untracked directories are also returned in
+ `entries[]`.
+
`DIR_COLLECT_IGNORED`:::
Special mode for git-add. Return ignored files in `ignored[]` and
diff --git a/Documentation/technical/api-parse-options.txt b/Documentation/technical/api-parse-options.txt
index 36768b479e..829b558110 100644
--- a/Documentation/technical/api-parse-options.txt
+++ b/Documentation/technical/api-parse-options.txt
@@ -183,13 +183,13 @@ There are some macros to easily define options:
scale the provided value by 1024, 1024^2 or 1024^3 respectively.
The scaled value is put into `unsigned_long_var`.
-`OPT_DATE(short, long, &int_var, description)`::
+`OPT_DATE(short, long, &timestamp_t_var, description)`::
Introduce an option with date argument, see `approxidate()`.
- The timestamp is put into `int_var`.
+ The timestamp is put into `timestamp_t_var`.
-`OPT_EXPIRY_DATE(short, long, &int_var, description)`::
+`OPT_EXPIRY_DATE(short, long, &timestamp_t_var, description)`::
Introduce an option with expiry date argument, see `parse_expiry_date()`.
- The timestamp is put into `int_var`.
+ The timestamp is put into `timestamp_t_var`.
`OPT_CALLBACK(short, long, &var, arg_str, description, func_ptr)`::
Introduce an option with argument.
diff --git a/Documentation/technical/api-sub-process.txt b/Documentation/technical/api-sub-process.txt
new file mode 100644
index 0000000000..793508cf3e
--- /dev/null
+++ b/Documentation/technical/api-sub-process.txt
@@ -0,0 +1,59 @@
+sub-process API
+===============
+
+The sub-process API makes it possible to run background sub-processes
+for the entire lifetime of a Git invocation. If Git needs to communicate
+with an external process multiple times, then this can reduces the process
+invocation overhead. Git and the sub-process communicate through stdin and
+stdout.
+
+The sub-processes are kept in a hashmap by command name and looked up
+via the subprocess_find_entry function. If an existing instance can not
+be found then a new process should be created and started. When the
+parent git command terminates, all sub-processes are also terminated.
+
+This API is based on the run-command API.
+
+Data structures
+---------------
+
+* `struct subprocess_entry`
+
+The sub-process structure. Members should not be accessed directly.
+
+Types
+-----
+
+'int(*subprocess_start_fn)(struct subprocess_entry *entry)'::
+
+ User-supplied function to initialize the sub-process. This is
+ typically used to negotiate the interface version and capabilities.
+
+
+Functions
+---------
+
+`cmd2process_cmp`::
+
+ Function to test two subprocess hashmap entries for equality.
+
+`subprocess_start`::
+
+ Start a subprocess and add it to the subprocess hashmap.
+
+`subprocess_stop`::
+
+ Kill a subprocess and remove it from the subprocess hashmap.
+
+`subprocess_find_entry`::
+
+ Find a subprocess in the subprocess hashmap.
+
+`subprocess_get_child_process`::
+
+ Get the underlying `struct child_process` from a subprocess.
+
+`subprocess_read_status`::
+
+ Helper function to read packets looking for the last "status=<foo>"
+ key/value pair.
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
index 5b6722840e..4f94fc7574 100755
--- a/GIT-VERSION-GEN
+++ b/GIT-VERSION-GEN
@@ -1,7 +1,7 @@
#!/bin/sh
GVF=GIT-VERSION-FILE
-DEF_VER=v2.13.1
+DEF_VER=v2.13.GIT
LF='
'
diff --git a/Makefile b/Makefile
index ffa6da71b7..7c621f7f76 100644
--- a/Makefile
+++ b/Makefile
@@ -24,8 +24,10 @@ all::
# Define NO_OPENSSL environment variable if you do not have OpenSSL.
# This also implies BLK_SHA1.
#
-# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
-# able to use Perl-compatible regular expressions.
+# Define USE_LIBPCRE if you have and want to use libpcre. Various
+# commands such as log and grep offer runtime options to use
+# Perl-compatible regular expressions instead of standard or extended
+# POSIX regular expressions.
#
# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
@@ -718,6 +720,7 @@ LIB_OBJS += argv-array.o
LIB_OBJS += attr.o
LIB_OBJS += base85.o
LIB_OBJS += bisect.o
+LIB_OBJS += blame.o
LIB_OBJS += blob.o
LIB_OBJS += branch.o
LIB_OBJS += bulk-checkin.o
@@ -842,6 +845,7 @@ LIB_OBJS += streaming.o
LIB_OBJS += string-list.o
LIB_OBJS += submodule.o
LIB_OBJS += submodule-config.o
+LIB_OBJS += sub-process.o
LIB_OBJS += symlinks.o
LIB_OBJS += tag.o
LIB_OBJS += tempfile.o
@@ -1086,7 +1090,7 @@ ifdef NO_LIBGEN_H
endif
ifdef USE_LIBPCRE
- BASIC_CFLAGS += -DUSE_LIBPCRE
+ BASIC_CFLAGS += -DUSE_LIBPCRE1
ifdef LIBPCREDIR
BASIC_CFLAGS += -I$(LIBPCREDIR)/include
EXTLIBS += -L$(LIBPCREDIR)/$(lib) $(CC_LD_DYNPATH)$(LIBPCREDIR)/$(lib)
@@ -2245,8 +2249,9 @@ GIT-BUILD-OPTIONS: FORCE
@echo TAR=\''$(subst ','\'',$(subst ','\'',$(TAR)))'\' >>$@+
@echo NO_CURL=\''$(subst ','\'',$(subst ','\'',$(NO_CURL)))'\' >>$@+
@echo NO_EXPAT=\''$(subst ','\'',$(subst ','\'',$(NO_EXPAT)))'\' >>$@+
- @echo USE_LIBPCRE=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+
+ @echo USE_LIBPCRE1=\''$(subst ','\'',$(subst ','\'',$(USE_LIBPCRE)))'\' >>$@+
@echo NO_PERL=\''$(subst ','\'',$(subst ','\'',$(NO_PERL)))'\' >>$@+
+ @echo NO_PTHREADS=\''$(subst ','\'',$(subst ','\'',$(NO_PTHREADS)))'\' >>$@+
@echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
@echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
@echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
@@ -2277,6 +2282,9 @@ endif
ifdef GIT_PERF_MAKE_OPTS
@echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
endif
+ifdef GIT_PERF_MAKE_COMMAND
+ @echo GIT_PERF_MAKE_COMMAND=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_COMMAND)))'\' >>$@+
+endif
ifdef GIT_INTEROP_MAKE_OPTS
@echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
endif
diff --git a/RelNotes b/RelNotes
index 52c8e104fa..983f6689bd 120000
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.13.1.txt \ No newline at end of file
+Documentation/RelNotes/2.14.0.txt \ No newline at end of file
diff --git a/archive-tar.c b/archive-tar.c
index 380e3aedd2..073e60ebd3 100644
--- a/archive-tar.c
+++ b/archive-tar.c
@@ -27,10 +27,13 @@ static int write_tar_filter_archive(const struct archiver *ar,
*/
#if ULONG_MAX == 0xFFFFFFFF
#define USTAR_MAX_SIZE ULONG_MAX
-#define USTAR_MAX_MTIME ULONG_MAX
#else
#define USTAR_MAX_SIZE 077777777777UL
-#define USTAR_MAX_MTIME 077777777777UL
+#endif
+#if TIME_MAX == 0xFFFFFFFF
+#define USTAR_MAX_MTIME TIME_MAX
+#else
+#define USTAR_MAX_MTIME 077777777777ULL
#endif
/* writes out the whole block, but only if it is full */
diff --git a/archive-zip.c b/archive-zip.c
index b429a8d974..27563e9e26 100644
--- a/archive-zip.c
+++ b/archive-zip.c
@@ -11,16 +11,14 @@
static int zip_date;
static int zip_time;
-static unsigned char *zip_dir;
-static unsigned int zip_dir_size;
+/* We only care about the "buf" part here. */
+static struct strbuf zip_dir;
-static unsigned int zip_offset;
-static unsigned int zip_dir_offset;
+static uintmax_t zip_offset;
static uint64_t zip_dir_entries;
static unsigned int max_creator_version;
-#define ZIP_DIRECTORY_MIN_SIZE (1024 * 1024)
#define ZIP_STREAM (1 << 3)
#define ZIP_UTF8 (1 << 11)
@@ -47,24 +45,11 @@ struct zip_data_desc {
unsigned char _end[1];
};
-struct zip_dir_header {
+struct zip64_data_desc {
unsigned char magic[4];
- unsigned char creator_version[2];
- unsigned char version[2];
- unsigned char flags[2];
- unsigned char compression_method[2];
- unsigned char mtime[2];
- unsigned char mdate[2];
unsigned char crc32[4];
- unsigned char compressed_size[4];
- unsigned char size[4];
- unsigned char filename_length[2];
- unsigned char extra_length[2];
- unsigned char comment_length[2];
- unsigned char disk[2];
- unsigned char attr1[2];
- unsigned char attr2[4];
- unsigned char offset[4];
+ unsigned char compressed_size[8];
+ unsigned char size[8];
unsigned char _end[1];
};
@@ -88,6 +73,14 @@ struct zip_extra_mtime {
unsigned char _end[1];
};
+struct zip64_extra {
+ unsigned char magic[2];
+ unsigned char extra_size[2];
+ unsigned char size[8];
+ unsigned char compressed_size[8];
+ unsigned char _end[1];
+};
+
struct zip64_dir_trailer {
unsigned char magic[4];
unsigned char record_size[8];
@@ -117,11 +110,15 @@ struct zip64_dir_trailer_locator {
*/
#define ZIP_LOCAL_HEADER_SIZE offsetof(struct zip_local_header, _end)
#define ZIP_DATA_DESC_SIZE offsetof(struct zip_data_desc, _end)
+#define ZIP64_DATA_DESC_SIZE offsetof(struct zip64_data_desc, _end)
#define ZIP_DIR_HEADER_SIZE offsetof(struct zip_dir_header, _end)
#define ZIP_DIR_TRAILER_SIZE offsetof(struct zip_dir_trailer, _end)
#define ZIP_EXTRA_MTIME_SIZE offsetof(struct zip_extra_mtime, _end)
#define ZIP_EXTRA_MTIME_PAYLOAD_SIZE \
(ZIP_EXTRA_MTIME_SIZE - offsetof(struct zip_extra_mtime, flags))
+#define ZIP64_EXTRA_SIZE offsetof(struct zip64_extra, _end)
+#define ZIP64_EXTRA_PAYLOAD_SIZE \
+ (ZIP64_EXTRA_SIZE - offsetof(struct zip64_extra, size))
#define ZIP64_DIR_TRAILER_SIZE offsetof(struct zip64_dir_trailer, _end)
#define ZIP64_DIR_TRAILER_RECORD_SIZE \
(ZIP64_DIR_TRAILER_SIZE - \
@@ -168,6 +165,26 @@ static void copy_le16_clamp(unsigned char *dest, uint64_t n, int *clamped)
copy_le16(dest, clamp_max(n, 0xffff, clamped));
}
+static void copy_le32_clamp(unsigned char *dest, uint64_t n, int *clamped)
+{
+ copy_le32(dest, clamp_max(n, 0xffffffff, clamped));
+}
+
+static int strbuf_add_le(struct strbuf *sb, size_t size, uintmax_t n)
+{
+ while (size-- > 0) {
+ strbuf_addch(sb, n & 0xff);
+ n >>= 8;
+ }
+ return -!!n;
+}
+
+static uint32_t clamp32(uintmax_t n)
+{
+ const uintmax_t max = 0xffffffff;
+ return (n < max) ? n : max;
+}
+
static void *zlib_deflate_raw(void *data, unsigned long size,
int compression_level,
unsigned long *compressed_size)
@@ -205,23 +222,23 @@ static void write_zip_data_desc(unsigned long size,
unsigned long compressed_size,
unsigned long crc)
{
- struct zip_data_desc trailer;
-
- copy_le32(trailer.magic, 0x08074b50);
- copy_le32(trailer.crc32, crc);
- copy_le32(trailer.compressed_size, compressed_size);
- copy_le32(trailer.size, size);
- write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
-}
-
-static void set_zip_dir_data_desc(struct zip_dir_header *header,
- unsigned long size,
- unsigned long compressed_size,
- unsigned long crc)
-{
- copy_le32(header->crc32, crc);
- copy_le32(header->compressed_size, compressed_size);
- copy_le32(header->size, size);
+ if (size >= 0xffffffff || compressed_size >= 0xffffffff) {
+ struct zip64_data_desc trailer;
+ copy_le32(trailer.magic, 0x08074b50);
+ copy_le32(trailer.crc32, crc);
+ copy_le64(trailer.compressed_size, compressed_size);
+ copy_le64(trailer.size, size);
+ write_or_die(1, &trailer, ZIP64_DATA_DESC_SIZE);
+ zip_offset += ZIP64_DATA_DESC_SIZE;
+ } else {
+ struct zip_data_desc trailer;
+ copy_le32(trailer.magic, 0x08074b50);
+ copy_le32(trailer.crc32, crc);
+ copy_le32(trailer.compressed_size, compressed_size);
+ copy_le32(trailer.size, size);
+ write_or_die(1, &trailer, ZIP_DATA_DESC_SIZE);
+ zip_offset += ZIP_DATA_DESC_SIZE;
+ }
}
static void set_zip_header_data_desc(struct zip_local_header *header,
@@ -263,12 +280,14 @@ static int write_zip_entry(struct archiver_args *args,
unsigned int mode)
{
struct zip_local_header header;
- struct zip_dir_header dirent;
+ uintmax_t offset = zip_offset;
struct zip_extra_mtime extra;
+ struct zip64_extra extra64;
+ size_t header_extra_size = ZIP_EXTRA_MTIME_SIZE;
+ int need_zip64_extra = 0;
unsigned long attr2;
unsigned long compressed_size;
unsigned long crc;
- unsigned long direntsize;
int method;
unsigned char *out;
void *deflated = NULL;
@@ -279,6 +298,9 @@ static int write_zip_entry(struct archiver_args *args,
int is_binary = -1;
const char *path_without_prefix = path + args->baselen;
unsigned int creator_version = 0;
+ unsigned int version_needed = 10;
+ size_t zip_dir_extra_size = ZIP_EXTRA_MTIME_SIZE;
+ size_t zip64_dir_extra_payload_size = 0;
crc = crc32(0, NULL, 0);
@@ -356,43 +378,43 @@ static int write_zip_entry(struct archiver_args *args,
extra.flags[0] = 1; /* just mtime */
copy_le32(extra.mtime, args->time);
- /* make sure we have enough free space in the dictionary */
- direntsize = ZIP_DIR_HEADER_SIZE + pathlen + ZIP_EXTRA_MTIME_SIZE;
- while (zip_dir_size < zip_dir_offset + direntsize) {
- zip_dir_size += ZIP_DIRECTORY_MIN_SIZE;
- zip_dir = xrealloc(zip_dir, zip_dir_size);
- }
+ if (size > 0xffffffff || compressed_size > 0xffffffff)
+ need_zip64_extra = 1;
+ if (stream && size > 0x7fffffff)
+ need_zip64_extra = 1;
- copy_le32(dirent.magic, 0x02014b50);
- copy_le16(dirent.creator_version, creator_version);
- copy_le16(dirent.version, 10);
- copy_le16(dirent.flags, flags);
- copy_le16(dirent.compression_method, method);
- copy_le16(dirent.mtime, zip_time);
- copy_le16(dirent.mdate, zip_date);
- set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
- copy_le16(dirent.filename_length, pathlen);
- copy_le16(dirent.extra_length, ZIP_EXTRA_MTIME_SIZE);
- copy_le16(dirent.comment_length, 0);
- copy_le16(dirent.disk, 0);
- copy_le32(dirent.attr2, attr2);
- copy_le32(dirent.offset, zip_offset);
+ if (need_zip64_extra)
+ version_needed = 45;
copy_le32(header.magic, 0x04034b50);
- copy_le16(header.version, 10);
+ copy_le16(header.version, version_needed);
copy_le16(header.flags, flags);
copy_le16(header.compression_method, method);
copy_le16(header.mtime, zip_time);
copy_le16(header.mdate, zip_date);
- set_zip_header_data_desc(&header, size, compressed_size, crc);
+ if (need_zip64_extra) {
+ set_zip_header_data_desc(&header, 0xffffffff, 0xffffffff, crc);
+ header_extra_size += ZIP64_EXTRA_SIZE;
+ } else {
+ set_zip_header_data_desc(&header, size, compressed_size, crc);
+ }
copy_le16(header.filename_length, pathlen);
- copy_le16(header.extra_length, ZIP_EXTRA_MTIME_SIZE);
+ copy_le16(header.extra_length, header_extra_size);
write_or_die(1, &header, ZIP_LOCAL_HEADER_SIZE);
zip_offset += ZIP_LOCAL_HEADER_SIZE;
write_or_die(1, path, pathlen);
zip_offset += pathlen;
write_or_die(1, &extra, ZIP_EXTRA_MTIME_SIZE);
zip_offset += ZIP_EXTRA_MTIME_SIZE;
+ if (need_zip64_extra) {
+ copy_le16(extra64.magic, 0x0001);
+ copy_le16(extra64.extra_size, ZIP64_EXTRA_PAYLOAD_SIZE);
+ copy_le64(extra64.size, size);
+ copy_le64(extra64.compressed_size, compressed_size);
+ write_or_die(1, &extra64, ZIP64_EXTRA_SIZE);
+ zip_offset += ZIP64_EXTRA_SIZE;
+ }
+
if (stream && method == 0) {
unsigned char buf[STREAM_BUFFER_SIZE];
ssize_t readlen;
@@ -415,9 +437,6 @@ static int write_zip_entry(struct archiver_args *args,
zip_offset += compressed_size;
write_zip_data_desc(size, compressed_size, crc);
- zip_offset += ZIP_DATA_DESC_SIZE;
-
- set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
} else if (stream && method == 8) {
unsigned char buf[STREAM_BUFFER_SIZE];
ssize_t readlen;
@@ -473,9 +492,6 @@ static int write_zip_entry(struct archiver_args *args,
zip_offset += compressed_size;
write_zip_data_desc(size, compressed_size, crc);
- zip_offset += ZIP_DATA_DESC_SIZE;
-
- set_zip_dir_data_desc(&dirent, size, compressed_size, crc);
} else if (compressed_size > 0) {
write_or_die(1, out, compressed_size);
zip_offset += compressed_size;
@@ -484,14 +500,46 @@ static int write_zip_entry(struct archiver_args *args,
free(deflated);
free(buffer);
- copy_le16(dirent.attr1, !is_binary);
+ if (compressed_size > 0xffffffff || size > 0xffffffff ||
+ offset > 0xffffffff) {
+ if (compressed_size >= 0xffffffff)
+ zip64_dir_extra_payload_size += 8;
+ if (size >= 0xffffffff)
+ zip64_dir_extra_payload_size += 8;
+ if (offset >= 0xffffffff)
+ zip64_dir_extra_payload_size += 8;
+ zip_dir_extra_size += 2 + 2 + zip64_dir_extra_payload_size;
+ }
- memcpy(zip_dir + zip_dir_offset, &dirent, ZIP_DIR_HEADER_SIZE);
- zip_dir_offset += ZIP_DIR_HEADER_SIZE;
- memcpy(zip_dir + zip_dir_offset, path, pathlen);
- zip_dir_offset += pathlen;
- memcpy(zip_dir + zip_dir_offset, &extra, ZIP_EXTRA_MTIME_SIZE);
- zip_dir_offset += ZIP_EXTRA_MTIME_SIZE;
+ strbuf_add_le(&zip_dir, 4, 0x02014b50); /* magic */
+ strbuf_add_le(&zip_dir, 2, creator_version);
+ strbuf_add_le(&zip_dir, 2, version_needed);
+ strbuf_add_le(&zip_dir, 2, flags);
+ strbuf_add_le(&zip_dir, 2, method);
+ strbuf_add_le(&zip_dir, 2, zip_time);
+ strbuf_add_le(&zip_dir, 2, zip_date);
+ strbuf_add_le(&zip_dir, 4, crc);
+ strbuf_add_le(&zip_dir, 4, clamp32(compressed_size));
+ strbuf_add_le(&zip_dir, 4, clamp32(size));
+ strbuf_add_le(&zip_dir, 2, pathlen);
+ strbuf_add_le(&zip_dir, 2, zip_dir_extra_size);
+ strbuf_add_le(&zip_dir, 2, 0); /* comment length */
+ strbuf_add_le(&zip_dir, 2, 0); /* disk */
+ strbuf_add_le(&zip_dir, 2, !is_binary);
+ strbuf_add_le(&zip_dir, 4, attr2);
+ strbuf_add_le(&zip_dir, 4, clamp32(offset));
+ strbuf_add(&zip_dir, path, pathlen);
+ strbuf_add(&zip_dir, &extra, ZIP_EXTRA_MTIME_SIZE);
+ if (zip64_dir_extra_payload_size) {
+ strbuf_add_le(&zip_dir, 2, 0x0001); /* magic */
+ strbuf_add_le(&zip_dir, 2, zip64_dir_extra_payload_size);
+ if (size >= 0xffffffff)
+ strbuf_add_le(&zip_dir, 8, size);
+ if (compressed_size >= 0xffffffff)
+ strbuf_add_le(&zip_dir, 8, compressed_size);
+ if (offset >= 0xffffffff)
+ strbuf_add_le(&zip_dir, 8, offset);
+ }
zip_dir_entries++;
return 0;
@@ -510,12 +558,12 @@ static void write_zip64_trailer(void)
copy_le32(trailer64.directory_start_disk, 0);
copy_le64(trailer64.entries_on_this_disk, zip_dir_entries);
copy_le64(trailer64.entries, zip_dir_entries);
- copy_le64(trailer64.size, zip_dir_offset);
+ copy_le64(trailer64.size, zip_dir.len);
copy_le64(trailer64.offset, zip_offset);
copy_le32(locator64.magic, 0x07064b50);
copy_le32(locator64.disk, 0);
- copy_le64(locator64.offset, zip_offset + zip_dir_offset);
+ copy_le64(locator64.offset, zip_offset + zip_dir.len);
copy_le32(locator64.number_of_disks, 1);
write_or_die(1, &trailer64, ZIP64_DIR_TRAILER_SIZE);
@@ -533,11 +581,11 @@ static void write_zip_trailer(const unsigned char *sha1)
copy_le16_clamp(trailer.entries_on_this_disk, zip_dir_entries,
&clamped);
copy_le16_clamp(trailer.entries, zip_dir_entries, &clamped);
- copy_le32(trailer.size, zip_dir_offset);
- copy_le32(trailer.offset, zip_offset);
+ copy_le32(trailer.size, zip_dir.len);
+ copy_le32_clamp(trailer.offset, zip_offset, &clamped);
copy_le16(trailer.comment_length, sha1 ? GIT_SHA1_HEXSZ : 0);
- write_or_die(1, zip_dir, zip_dir_offset);
+ write_or_die(1, zip_dir.buf, zip_dir.len);
if (clamped)
write_zip64_trailer();
write_or_die(1, &trailer, ZIP_DIR_TRAILER_SIZE);
@@ -545,9 +593,17 @@ static void write_zip_trailer(const unsigned char *sha1)
write_or_die(1, sha1_to_hex(sha1), GIT_SHA1_HEXSZ);
}
-static void dos_time(time_t *time, int *dos_date, int *dos_time)
+static void dos_time(timestamp_t *timestamp, int *dos_date, int *dos_time)
{
- struct tm *t = localtime(time);
+ time_t time;
+ struct tm *t;
+
+ if (date_overflows(*timestamp))
+ die("timestamp too large for this system: %"PRItime,
+ *timestamp);
+ time = (time_t)*timestamp;
+ t = localtime(&time);
+ *timestamp = time;
*dos_date = t->tm_mday + (t->tm_mon + 1) * 32 +
(t->tm_year + 1900 - 1980) * 512;
@@ -568,14 +624,13 @@ static int write_zip_archive(const struct archiver *ar,
dos_time(&args->time, &zip_date, &zip_time);
- zip_dir = xmalloc(ZIP_DIRECTORY_MIN_SIZE);
- zip_dir_size = ZIP_DIRECTORY_MIN_SIZE;
+ strbuf_init(&zip_dir, 0);
err = write_archive_entries(args, write_zip_entry);
if (!err)
write_zip_trailer(args->commit_sha1);
- free(zip_dir);
+ strbuf_release(&zip_dir);
return err;
}
diff --git a/archive.c b/archive.c
index 60b8891986..b15a922dab 100644
--- a/archive.c
+++ b/archive.c
@@ -360,7 +360,7 @@ static void parse_treeish_arg(const char **argv,
if (get_sha1(name, oid.hash))
die("Not a valid object name");
- commit = lookup_commit_reference_gently(oid.hash, 1);
+ commit = lookup_commit_reference_gently(&oid, 1);
if (commit) {
commit_sha1 = commit->object.oid.hash;
archive_time = commit->date;
@@ -369,7 +369,7 @@ static void parse_treeish_arg(const char **argv,
archive_time = time(NULL);
}
- tree = parse_tree_indirect(oid.hash);
+ tree = parse_tree_indirect(&oid);
if (tree == NULL)
die("not a tree object");
@@ -383,7 +383,7 @@ static void parse_treeish_arg(const char **argv,
if (err || !S_ISDIR(mode))
die("current working directory is untracked");
- tree = parse_tree_indirect(tree_oid.hash);
+ tree = parse_tree_indirect(&tree_oid);
}
ar_args->tree = tree;
ar_args->commit_sha1 = commit_sha1;
diff --git a/archive.h b/archive.h
index 415e0152e2..62d1d82c1a 100644
--- a/archive.h
+++ b/archive.h
@@ -9,7 +9,7 @@ struct archiver_args {
struct tree *tree;
const unsigned char *commit_sha1;
const struct commit *commit;
- time_t time;
+ timestamp_t time;
struct pathspec pathspec;
unsigned int verbose : 1;
unsigned int worktree_attributes : 1;
diff --git a/bisect.c b/bisect.c
index aaab6ada2b..c952df692b 100644
--- a/bisect.c
+++ b/bisect.c
@@ -705,7 +705,7 @@ static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
static struct commit *get_commit_reference(const struct object_id *oid)
{
- struct commit *r = lookup_commit_reference(oid->hash);
+ struct commit *r = lookup_commit_reference(oid);
if (!r)
die(_("Not a valid commit name %s"), oid_to_hex(oid));
return r;
diff --git a/blame.c b/blame.c
new file mode 100644
index 0000000000..843c845cba
--- /dev/null
+++ b/blame.c
@@ -0,0 +1,1863 @@
+#include "cache.h"
+#include "refs.h"
+#include "cache-tree.h"
+#include "mergesort.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "tag.h"
+#include "blame.h"
+
+void blame_origin_decref(struct blame_origin *o)
+{
+ if (o && --o->refcnt <= 0) {
+ struct blame_origin *p, *l = NULL;
+ if (o->previous)
+ blame_origin_decref(o->previous);
+ free(o->file.ptr);
+ /* Should be present exactly once in commit chain */
+ for (p = o->commit->util; p; l = p, p = p->next) {
+ if (p == o) {
+ if (l)
+ l->next = p->next;
+ else
+ o->commit->util = p->next;
+ free(o);
+ return;
+ }
+ }
+ die("internal error in blame_origin_decref");
+ }
+}
+
+/*
+ * Given a commit and a path in it, create a new origin structure.
+ * The callers that add blame to the scoreboard should use
+ * get_origin() to obtain shared, refcounted copy instead of calling
+ * this function directly.
+ */
+static struct blame_origin *make_origin(struct commit *commit, const char *path)
+{
+ struct blame_origin *o;
+ FLEX_ALLOC_STR(o, path, path);
+ o->commit = commit;
+ o->refcnt = 1;
+ o->next = commit->util;
+ commit->util = o;
+ return o;
+}
+
+/*
+ * Locate an existing origin or create a new one.
+ * This moves the origin to front position in the commit util list.
+ */
+static struct blame_origin *get_origin(struct commit *commit, const char *path)
+{
+ struct blame_origin *o, *l;
+
+ for (o = commit->util, l = NULL; o; l = o, o = o->next) {
+ if (!strcmp(o->path, path)) {
+ /* bump to front */
+ if (l) {
+ l->next = o->next;
+ o->next = commit->util;
+ commit->util = o;
+ }
+ return blame_origin_incref(o);
+ }
+ }
+ return make_origin(commit, path);
+}
+
+
+
+static void verify_working_tree_path(struct commit *work_tree, const char *path)
+{
+ struct commit_list *parents;
+ int pos;
+
+ for (parents = work_tree->parents; parents; parents = parents->next) {
+ const struct object_id *commit_oid = &parents->item->object.oid;
+ struct object_id blob_oid;
+ unsigned mode;
+
+ if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
+ sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
+ return;
+ }
+
+ pos = cache_name_pos(path, strlen(path));
+ if (pos >= 0)
+ ; /* path is in the index */
+ else if (-1 - pos < active_nr &&
+ !strcmp(active_cache[-1 - pos]->name, path))
+ ; /* path is in the index, unmerged */
+ else
+ die("no such path '%s' in HEAD", path);
+}
+
+static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
+{
+ struct commit *parent;
+
+ parent = lookup_commit_reference(oid);
+ if (!parent)
+ die("no such commit %s", oid_to_hex(oid));
+ return &commit_list_insert(parent, tail)->next;
+}
+
+static void append_merge_parents(struct commit_list **tail)
+{
+ int merge_head;
+ struct strbuf line = STRBUF_INIT;
+
+ merge_head = open(git_path_merge_head(), O_RDONLY);
+ if (merge_head < 0) {
+ if (errno == ENOENT)
+ return;
+ die("cannot open '%s' for reading", git_path_merge_head());
+ }
+
+ while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
+ struct object_id oid;
+ if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
+ die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
+ tail = append_parent(tail, &oid);
+ }
+ close(merge_head);
+ strbuf_release(&line);
+}
+
+/*
+ * This isn't as simple as passing sb->buf and sb->len, because we
+ * want to transfer ownership of the buffer to the commit (so we
+ * must use detach).
+ */
+static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
+{
+ size_t len;
+ void *buf = strbuf_detach(sb, &len);
+ set_commit_buffer(c, buf, len);
+}
+
+/*
+ * 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 diff_options *opt,
+ const char *path,
+ const char *contents_from)
+{
+ struct commit *commit;
+ struct blame_origin *origin;
+ struct commit_list **parent_tail, *parent;
+ struct object_id head_oid;
+ struct strbuf buf = STRBUF_INIT;
+ const char *ident;
+ time_t now;
+ int size, len;
+ struct cache_entry *ce;
+ unsigned mode;
+ struct strbuf msg = STRBUF_INIT;
+
+ read_cache();
+ time(&now);
+ commit = alloc_commit_node();
+ commit->object.parsed = 1;
+ commit->date = now;
+ parent_tail = &commit->parents;
+
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
+ die("no such ref: HEAD");
+
+ parent_tail = append_parent(parent_tail, &head_oid);
+ append_merge_parents(parent_tail);
+ verify_working_tree_path(commit, path);
+
+ origin = make_origin(commit, path);
+
+ ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
+ strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
+ for (parent = commit->parents; parent; parent = parent->next)
+ strbuf_addf(&msg, "parent %s\n",
+ oid_to_hex(&parent->item->object.oid));
+ strbuf_addf(&msg,
+ "author %s\n"
+ "committer %s\n\n"
+ "Version of %s from %s\n",
+ ident, ident, path,
+ (!contents_from ? path :
+ (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
+ set_commit_buffer_from_strbuf(commit, &msg);
+
+ if (!contents_from || strcmp("-", contents_from)) {
+ struct stat st;
+ const char *read_from;
+ char *buf_ptr;
+ unsigned long buf_len;
+
+ if (contents_from) {
+ if (stat(contents_from, &st) < 0)
+ die_errno("Cannot stat '%s'", contents_from);
+ read_from = contents_from;
+ }
+ else {
+ if (lstat(path, &st) < 0)
+ die_errno("Cannot lstat '%s'", path);
+ read_from = path;
+ }
+ mode = canon_mode(st.st_mode);
+
+ switch (st.st_mode & S_IFMT) {
+ case S_IFREG:
+ if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ textconv_object(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);
+ break;
+ case S_IFLNK:
+ if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
+ die_errno("cannot readlink '%s'", read_from);
+ break;
+ default:
+ die("unsupported file type %s", read_from);
+ }
+ }
+ else {
+ /* Reading from stdin */
+ mode = 0;
+ if (strbuf_read(&buf, 0, 0) < 0)
+ die_errno("failed to read from stdin");
+ }
+ convert_to_git(path, buf.buf, buf.len, &buf, 0);
+ origin->file.ptr = buf.buf;
+ origin->file.size = buf.len;
+ pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
+
+ /*
+ * Read the current index, replace the path entry with
+ * origin->blob_sha1 without mucking with its mode or type
+ * bits; we are not going to write this index out -- we just
+ * want to run "diff-index --cached".
+ */
+ discard_cache();
+ read_cache();
+
+ len = strlen(path);
+ if (!mode) {
+ int pos = cache_name_pos(path, len);
+ if (0 <= pos)
+ mode = active_cache[pos]->ce_mode;
+ else
+ /* Let's not bother reading from HEAD tree */
+ mode = S_IFREG | 0644;
+ }
+ size = cache_entry_size(len);
+ ce = xcalloc(1, size);
+ 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_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
+
+ cache_tree_invalidate_path(&the_index, path);
+
+ return commit;
+}
+
+
+
+static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
+ xdl_emit_hunk_consume_func_t hunk_func, void *cb_data, int xdl_opts)
+{
+ xpparam_t xpp = {0};
+ xdemitconf_t xecfg = {0};
+ xdemitcb_t ecb = {NULL};
+
+ xpp.flags = xdl_opts;
+ xecfg.hunk_func = hunk_func;
+ ecb.priv = cb_data;
+ return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
+}
+
+/*
+ * Given an origin, prepare mmfile_t structure to be used by the
+ * diff machinery
+ */
+static void fill_origin_blob(struct diff_options *opt,
+ struct blame_origin *o, mmfile_t *file, int *num_read_blob)
+{
+ if (!o->file.ptr) {
+ enum object_type type;
+ unsigned long file_size;
+
+ (*num_read_blob)++;
+ if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
+ textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
+ ;
+ else
+ file->ptr = read_sha1_file(o->blob_oid.hash, &type,
+ &file_size);
+ file->size = file_size;
+
+ if (!file->ptr)
+ die("Cannot read blob %s for path %s",
+ oid_to_hex(&o->blob_oid),
+ o->path);
+ o->file = *file;
+ }
+ else
+ *file = o->file;
+}
+
+static void drop_origin_blob(struct blame_origin *o)
+{
+ if (o->file.ptr) {
+ free(o->file.ptr);
+ o->file.ptr = NULL;
+ }
+}
+
+/*
+ * Any merge of blames happens on lists of blames that arrived via
+ * different parents in a single suspect. In this case, we want to
+ * sort according to the suspect line numbers as opposed to the final
+ * image line numbers. The function body is somewhat longish because
+ * it avoids unnecessary writes.
+ */
+
+static struct blame_entry *blame_merge(struct blame_entry *list1,
+ struct blame_entry *list2)
+{
+ struct blame_entry *p1 = list1, *p2 = list2,
+ **tail = &list1;
+
+ if (!p1)
+ return p2;
+ if (!p2)
+ return p1;
+
+ if (p1->s_lno <= p2->s_lno) {
+ do {
+ tail = &p1->next;
+ if ((p1 = *tail) == NULL) {
+ *tail = p2;
+ return list1;
+ }
+ } while (p1->s_lno <= p2->s_lno);
+ }
+ for (;;) {
+ *tail = p2;
+ do {
+ tail = &p2->next;
+ if ((p2 = *tail) == NULL) {
+ *tail = p1;
+ return list1;
+ }
+ } while (p1->s_lno > p2->s_lno);
+ *tail = p1;
+ do {
+ tail = &p1->next;
+ if ((p1 = *tail) == NULL) {
+ *tail = p2;
+ return list1;
+ }
+ } while (p1->s_lno <= p2->s_lno);
+ }
+}
+
+static void *get_next_blame(const void *p)
+{
+ return ((struct blame_entry *)p)->next;
+}
+
+static void set_next_blame(void *p1, void *p2)
+{
+ ((struct blame_entry *)p1)->next = p2;
+}
+
+/*
+ * Final image line numbers are all different, so we don't need a
+ * three-way comparison here.
+ */
+
+static int compare_blame_final(const void *p1, const void *p2)
+{
+ return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno
+ ? 1 : -1;
+}
+
+static int compare_blame_suspect(const void *p1, const void *p2)
+{
+ const struct blame_entry *s1 = p1, *s2 = p2;
+ /*
+ * to allow for collating suspects, we sort according to the
+ * respective pointer value as the primary sorting criterion.
+ * The actual relation is pretty unimportant as long as it
+ * establishes a total order. Comparing as integers gives us
+ * that.
+ */
+ if (s1->suspect != s2->suspect)
+ return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;
+ if (s1->s_lno == s2->s_lno)
+ return 0;
+ return s1->s_lno > s2->s_lno ? 1 : -1;
+}
+
+void blame_sort_final(struct blame_scoreboard *sb)
+{
+ sb->ent = llist_mergesort(sb->ent, get_next_blame, set_next_blame,
+ compare_blame_final);
+}
+
+static int compare_commits_by_reverse_commit_date(const void *a,
+ const void *b,
+ void *c)
+{
+ return -compare_commits_by_commit_date(a, b, c);
+}
+
+/*
+ * For debugging -- origin is refcounted, and this asserts that
+ * we do not underflow.
+ */
+static void sanity_check_refcnt(struct blame_scoreboard *sb)
+{
+ int baa = 0;
+ struct blame_entry *ent;
+
+ for (ent = sb->ent; ent; ent = ent->next) {
+ /* Nobody should have zero or negative refcnt */
+ if (ent->suspect->refcnt <= 0) {
+ fprintf(stderr, "%s in %s has negative refcnt %d\n",
+ ent->suspect->path,
+ oid_to_hex(&ent->suspect->commit->object.oid),
+ ent->suspect->refcnt);
+ baa = 1;
+ }
+ }
+ if (baa)
+ sb->on_sanity_fail(sb, baa);
+}
+
+/*
+ * If two blame entries that are next to each other came from
+ * contiguous lines in the same origin (i.e. <commit, path> pair),
+ * merge them together.
+ */
+void blame_coalesce(struct blame_scoreboard *sb)
+{
+ struct blame_entry *ent, *next;
+
+ for (ent = sb->ent; ent && (next = ent->next); ent = next) {
+ if (ent->suspect == next->suspect &&
+ ent->s_lno + ent->num_lines == next->s_lno) {
+ ent->num_lines += next->num_lines;
+ ent->next = next->next;
+ blame_origin_decref(next->suspect);
+ free(next);
+ ent->score = 0;
+ next = ent; /* again */
+ }
+ }
+
+ if (sb->debug) /* sanity */
+ sanity_check_refcnt(sb);
+}
+
+/*
+ * Merge the given sorted list of blames into a preexisting origin.
+ * If there were no previous blames to that commit, it is entered into
+ * the commit priority queue of the score board.
+ */
+
+static void queue_blames(struct blame_scoreboard *sb, struct blame_origin *porigin,
+ struct blame_entry *sorted)
+{
+ if (porigin->suspects)
+ porigin->suspects = blame_merge(porigin->suspects, sorted);
+ else {
+ struct blame_origin *o;
+ for (o = porigin->commit->util; o; o = o->next) {
+ if (o->suspects) {
+ porigin->suspects = sorted;
+ return;
+ }
+ }
+ porigin->suspects = sorted;
+ prio_queue_put(&sb->commits, porigin->commit);
+ }
+}
+
+/*
+ * Fill the blob_sha1 field of an origin if it hasn't, so that later
+ * call to fill_origin_blob() can use it to locate the data. blob_sha1
+ * for an origin is also used to pass the blame for the entire file to
+ * the parent to detect the case where a child's blob is identical to
+ * that of its parent's.
+ *
+ * This also fills origin->mode for corresponding tree path.
+ */
+static int fill_blob_sha1_and_mode(struct blame_origin *origin)
+{
+ if (!is_null_oid(&origin->blob_oid))
+ return 0;
+ if (get_tree_entry(origin->commit->object.oid.hash,
+ origin->path,
+ origin->blob_oid.hash, &origin->mode))
+ goto error_out;
+ if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)
+ goto error_out;
+ return 0;
+ error_out:
+ oidclr(&origin->blob_oid);
+ origin->mode = S_IFINVALID;
+ return -1;
+}
+
+/*
+ * 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)
+{
+ struct blame_origin *porigin;
+ struct diff_options diff_opts;
+ const char *paths[2];
+
+ /* First check any existing origins */
+ for (porigin = parent->util; porigin; porigin = porigin->next)
+ if (!strcmp(porigin->path, origin->path)) {
+ /*
+ * The same path between origin and its parent
+ * without renaming -- the most common case.
+ */
+ return blame_origin_incref (porigin);
+ }
+
+ /* See if the origin->path is different between parent
+ * and origin first. Most of the time they are the
+ * same and diff-tree is fairly efficient about this.
+ */
+ diff_setup(&diff_opts);
+ DIFF_OPT_SET(&diff_opts, RECURSIVE);
+ diff_opts.detect_rename = 0;
+ diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ paths[0] = origin->path;
+ paths[1] = NULL;
+
+ parse_pathspec(&diff_opts.pathspec,
+ PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
+ PATHSPEC_LITERAL_PATH, "", paths);
+ diff_setup_done(&diff_opts);
+
+ if (is_null_oid(&origin->commit->object.oid))
+ do_diff_cache(&parent->tree->object.oid, &diff_opts);
+ else
+ diff_tree_sha1(parent->tree->object.oid.hash,
+ origin->commit->tree->object.oid.hash,
+ "", &diff_opts);
+ diffcore_std(&diff_opts);
+
+ if (!diff_queued_diff.nr) {
+ /* The path is the same as parent */
+ porigin = get_origin(parent, origin->path);
+ oidcpy(&porigin->blob_oid, &origin->blob_oid);
+ porigin->mode = origin->mode;
+ } else {
+ /*
+ * Since origin->path is a pathspec, if the parent
+ * commit had it as a directory, we will see a whole
+ * bunch of deletion of files in the directory that we
+ * do not care about.
+ */
+ int i;
+ struct diff_filepair *p = NULL;
+ for (i = 0; i < diff_queued_diff.nr; i++) {
+ const char *name;
+ p = diff_queued_diff.queue[i];
+ name = p->one->path ? p->one->path : p->two->path;
+ if (!strcmp(name, origin->path))
+ break;
+ }
+ if (!p)
+ die("internal error in blame::find_origin");
+ switch (p->status) {
+ default:
+ die("internal error in blame::find_origin (%c)",
+ p->status);
+ case 'M':
+ porigin = get_origin(parent, origin->path);
+ oidcpy(&porigin->blob_oid, &p->one->oid);
+ porigin->mode = p->one->mode;
+ break;
+ case 'A':
+ case 'T':
+ /* Did not exist in parent, or type changed */
+ break;
+ }
+ }
+ diff_flush(&diff_opts);
+ clear_pathspec(&diff_opts.pathspec);
+ return porigin;
+}
+
+/*
+ * 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)
+{
+ struct blame_origin *porigin = NULL;
+ struct diff_options diff_opts;
+ int i;
+
+ diff_setup(&diff_opts);
+ DIFF_OPT_SET(&diff_opts, RECURSIVE);
+ diff_opts.detect_rename = DIFF_DETECT_RENAME;
+ diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+ diff_opts.single_follow = origin->path;
+ diff_setup_done(&diff_opts);
+
+ if (is_null_oid(&origin->commit->object.oid))
+ do_diff_cache(&parent->tree->object.oid, &diff_opts);
+ else
+ diff_tree_sha1(parent->tree->object.oid.hash,
+ origin->commit->tree->object.oid.hash,
+ "", &diff_opts);
+ diffcore_std(&diff_opts);
+
+ for (i = 0; i < diff_queued_diff.nr; i++) {
+ struct diff_filepair *p = diff_queued_diff.queue[i];
+ if ((p->status == 'R' || p->status == 'C') &&
+ !strcmp(p->two->path, origin->path)) {
+ porigin = get_origin(parent, p->one->path);
+ oidcpy(&porigin->blob_oid, &p->one->oid);
+ porigin->mode = p->one->mode;
+ break;
+ }
+ }
+ diff_flush(&diff_opts);
+ clear_pathspec(&diff_opts.pathspec);
+ return porigin;
+}
+
+/*
+ * Append a new blame entry to a given output queue.
+ */
+static void add_blame_entry(struct blame_entry ***queue,
+ const struct blame_entry *src)
+{
+ struct blame_entry *e = xmalloc(sizeof(*e));
+ memcpy(e, src, sizeof(*e));
+ blame_origin_incref(e->suspect);
+
+ e->next = **queue;
+ **queue = e;
+ *queue = &e->next;
+}
+
+/*
+ * src typically is on-stack; we want to copy the information in it to
+ * a malloced blame_entry that gets added to the given queue. The
+ * origin of dst loses a refcnt.
+ */
+static void dup_entry(struct blame_entry ***queue,
+ struct blame_entry *dst, struct blame_entry *src)
+{
+ blame_origin_incref(src->suspect);
+ blame_origin_decref(dst->suspect);
+ memcpy(dst, src, sizeof(*src));
+ dst->next = **queue;
+ **queue = dst;
+ *queue = &dst->next;
+}
+
+const char *blame_nth_line(struct blame_scoreboard *sb, long lno)
+{
+ return sb->final_buf + sb->lineno[lno];
+}
+
+/*
+ * It is known that lines between tlno to same came from parent, and e
+ * has an overlap with that range. it also is known that parent's
+ * line plno corresponds to e's line tlno.
+ *
+ * <---- e ----->
+ * <------>
+ * <------------>
+ * <------------>
+ * <------------------>
+ *
+ * Split e into potentially three parts; before this chunk, the chunk
+ * to be blamed for the parent, and after that portion.
+ */
+static void split_overlap(struct blame_entry *split,
+ struct blame_entry *e,
+ int tlno, int plno, int same,
+ struct blame_origin *parent)
+{
+ int chunk_end_lno;
+ memset(split, 0, sizeof(struct blame_entry [3]));
+
+ if (e->s_lno < tlno) {
+ /* there is a pre-chunk part not blamed on parent */
+ split[0].suspect = blame_origin_incref(e->suspect);
+ split[0].lno = e->lno;
+ split[0].s_lno = e->s_lno;
+ split[0].num_lines = tlno - e->s_lno;
+ split[1].lno = e->lno + tlno - e->s_lno;
+ split[1].s_lno = plno;
+ }
+ else {
+ split[1].lno = e->lno;
+ split[1].s_lno = plno + (e->s_lno - tlno);
+ }
+
+ if (same < e->s_lno + e->num_lines) {
+ /* there is a post-chunk part not blamed on parent */
+ split[2].suspect = blame_origin_incref(e->suspect);
+ split[2].lno = e->lno + (same - e->s_lno);
+ split[2].s_lno = e->s_lno + (same - e->s_lno);
+ split[2].num_lines = e->s_lno + e->num_lines - same;
+ chunk_end_lno = split[2].lno;
+ }
+ else
+ chunk_end_lno = e->lno + e->num_lines;
+ split[1].num_lines = chunk_end_lno - split[1].lno;
+
+ /*
+ * if it turns out there is nothing to blame the parent for,
+ * forget about the splitting. !split[1].suspect signals this.
+ */
+ if (split[1].num_lines < 1)
+ return;
+ split[1].suspect = blame_origin_incref(parent);
+}
+
+/*
+ * split_overlap() divided an existing blame e into up to three parts
+ * in split. Any assigned blame is moved to queue to
+ * reflect the split.
+ */
+static void split_blame(struct blame_entry ***blamed,
+ struct blame_entry ***unblamed,
+ struct blame_entry *split,
+ struct blame_entry *e)
+{
+ if (split[0].suspect && split[2].suspect) {
+ /* The first part (reuse storage for the existing entry e) */
+ dup_entry(unblamed, e, &split[0]);
+
+ /* The last part -- me */
+ add_blame_entry(unblamed, &split[2]);
+
+ /* ... and the middle part -- parent */
+ add_blame_entry(blamed, &split[1]);
+ }
+ else if (!split[0].suspect && !split[2].suspect)
+ /*
+ * The parent covers the entire area; reuse storage for
+ * e and replace it with the parent.
+ */
+ dup_entry(blamed, e, &split[1]);
+ else if (split[0].suspect) {
+ /* me and then parent */
+ dup_entry(unblamed, e, &split[0]);
+ add_blame_entry(blamed, &split[1]);
+ }
+ else {
+ /* parent and then me */
+ dup_entry(blamed, e, &split[1]);
+ add_blame_entry(unblamed, &split[2]);
+ }
+}
+
+/*
+ * After splitting the blame, the origins used by the
+ * on-stack blame_entry should lose one refcnt each.
+ */
+static void decref_split(struct blame_entry *split)
+{
+ int i;
+
+ for (i = 0; i < 3; i++)
+ blame_origin_decref(split[i].suspect);
+}
+
+/*
+ * reverse_blame reverses the list given in head, appending tail.
+ * That allows us to build lists in reverse order, then reverse them
+ * afterwards. This can be faster than building the list in proper
+ * order right away. The reason is that building in proper order
+ * requires writing a link in the _previous_ element, while building
+ * in reverse order just requires placing the list head into the
+ * _current_ element.
+ */
+
+static struct blame_entry *reverse_blame(struct blame_entry *head,
+ struct blame_entry *tail)
+{
+ while (head) {
+ struct blame_entry *next = head->next;
+ head->next = tail;
+ tail = head;
+ head = next;
+ }
+ return tail;
+}
+
+/*
+ * Process one hunk from the patch between the current suspect for
+ * blame_entry e and its parent. This first blames any unfinished
+ * entries before the chunk (which is where target and parent start
+ * differing) on the parent, and then splits blame entries at the
+ * start and at the end of the difference region. Since use of -M and
+ * -C options may lead to overlapping/duplicate source line number
+ * ranges, all we can rely on from sorting/merging is the order of the
+ * first suspect line number.
+ */
+static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
+ int tlno, int offset, int same,
+ struct blame_origin *parent)
+{
+ struct blame_entry *e = **srcq;
+ struct blame_entry *samep = NULL, *diffp = NULL;
+
+ while (e && e->s_lno < tlno) {
+ struct blame_entry *next = e->next;
+ /*
+ * current record starts before differing portion. If
+ * it reaches into it, we need to split it up and
+ * examine the second part separately.
+ */
+ if (e->s_lno + e->num_lines > tlno) {
+ /* Move second half to a new record */
+ int len = tlno - e->s_lno;
+ struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
+ n->suspect = e->suspect;
+ n->lno = e->lno + len;
+ n->s_lno = e->s_lno + len;
+ n->num_lines = e->num_lines - len;
+ e->num_lines = len;
+ e->score = 0;
+ /* Push new record to diffp */
+ n->next = diffp;
+ diffp = n;
+ } else
+ blame_origin_decref(e->suspect);
+ /* Pass blame for everything before the differing
+ * chunk to the parent */
+ e->suspect = blame_origin_incref(parent);
+ e->s_lno += offset;
+ e->next = samep;
+ samep = e;
+ e = next;
+ }
+ /*
+ * As we don't know how much of a common stretch after this
+ * diff will occur, the currently blamed parts are all that we
+ * can assign to the parent for now.
+ */
+
+ if (samep) {
+ **dstq = reverse_blame(samep, **dstq);
+ *dstq = &samep->next;
+ }
+ /*
+ * Prepend the split off portions: everything after e starts
+ * after the blameable portion.
+ */
+ e = reverse_blame(diffp, e);
+
+ /*
+ * Now retain records on the target while parts are different
+ * from the parent.
+ */
+ samep = NULL;
+ diffp = NULL;
+ while (e && e->s_lno < same) {
+ struct blame_entry *next = e->next;
+
+ /*
+ * If current record extends into sameness, need to split.
+ */
+ if (e->s_lno + e->num_lines > same) {
+ /*
+ * Move second half to a new record to be
+ * processed by later chunks
+ */
+ int len = same - e->s_lno;
+ struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
+ n->suspect = blame_origin_incref(e->suspect);
+ n->lno = e->lno + len;
+ n->s_lno = e->s_lno + len;
+ n->num_lines = e->num_lines - len;
+ e->num_lines = len;
+ e->score = 0;
+ /* Push new record to samep */
+ n->next = samep;
+ samep = n;
+ }
+ e->next = diffp;
+ diffp = e;
+ e = next;
+ }
+ **srcq = reverse_blame(diffp, reverse_blame(samep, e));
+ /* Move across elements that are in the unblamable portion */
+ if (diffp)
+ *srcq = &diffp->next;
+}
+
+struct blame_chunk_cb_data {
+ struct blame_origin *parent;
+ long offset;
+ struct blame_entry **dstq;
+ struct blame_entry **srcq;
+};
+
+/* diff chunks are from parent to target */
+static int blame_chunk_cb(long start_a, long count_a,
+ long start_b, long count_b, void *data)
+{
+ struct blame_chunk_cb_data *d = data;
+ if (start_a - start_b != d->offset)
+ die("internal error in blame::blame_chunk_cb");
+ blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,
+ start_b + count_b, d->parent);
+ d->offset = start_a + count_a - (start_b + count_b);
+ return 0;
+}
+
+/*
+ * We are looking at the origin 'target' and aiming to pass blame
+ * for the lines it is suspected to its parent. Run diff to find
+ * which lines came from parent and pass blame for them.
+ */
+static void pass_blame_to_parent(struct blame_scoreboard *sb,
+ struct blame_origin *target,
+ struct blame_origin *parent)
+{
+ mmfile_t file_p, file_o;
+ struct blame_chunk_cb_data d;
+ struct blame_entry *newdest = NULL;
+
+ if (!target->suspects)
+ return; /* nothing remains for this target */
+
+ d.parent = parent;
+ d.offset = 0;
+ d.dstq = &newdest; d.srcq = &target->suspects;
+
+ fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);
+ fill_origin_blob(&sb->revs->diffopt, target, &file_o, &sb->num_read_blob);
+ sb->num_get_patch++;
+
+ if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d, sb->xdl_opts))
+ die("unable to generate diff (%s -> %s)",
+ oid_to_hex(&parent->commit->object.oid),
+ oid_to_hex(&target->commit->object.oid));
+ /* The rest are the same as the parent */
+ blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
+ *d.dstq = NULL;
+ queue_blames(sb, parent, newdest);
+
+ return;
+}
+
+/*
+ * The lines in blame_entry after splitting blames many times can become
+ * very small and trivial, and at some point it becomes pointless to
+ * blame the parents. E.g. "\t\t}\n\t}\n\n" appears everywhere in any
+ * ordinary C program, and it is not worth to say it was copied from
+ * totally unrelated file in the parent.
+ *
+ * Compute how trivial the lines in the blame_entry are.
+ */
+unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e)
+{
+ unsigned score;
+ const char *cp, *ep;
+
+ if (e->score)
+ return e->score;
+
+ score = 1;
+ cp = blame_nth_line(sb, e->lno);
+ ep = blame_nth_line(sb, e->lno + e->num_lines);
+ while (cp < ep) {
+ unsigned ch = *((unsigned char *)cp);
+ if (isalnum(ch))
+ score++;
+ cp++;
+ }
+ e->score = score;
+ return score;
+}
+
+/*
+ * best_so_far[] and this[] are both a split of an existing blame_entry
+ * that passes blame to the parent. Maintain best_so_far the best split
+ * so far, by comparing this and best_so_far and copying this into
+ * bst_so_far as needed.
+ */
+static void copy_split_if_better(struct blame_scoreboard *sb,
+ struct blame_entry *best_so_far,
+ struct blame_entry *this)
+{
+ int i;
+
+ if (!this[1].suspect)
+ return;
+ if (best_so_far[1].suspect) {
+ if (blame_entry_score(sb, &this[1]) < blame_entry_score(sb, &best_so_far[1]))
+ return;
+ }
+
+ for (i = 0; i < 3; i++)
+ blame_origin_incref(this[i].suspect);
+ decref_split(best_so_far);
+ memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
+}
+
+/*
+ * We are looking at a part of the final image represented by
+ * ent (tlno and same are offset by ent->s_lno).
+ * tlno is where we are looking at in the final image.
+ * up to (but not including) same match preimage.
+ * plno is where we are looking at in the preimage.
+ *
+ * <-------------- final image ---------------------->
+ * <------ent------>
+ * ^tlno ^same
+ * <---------preimage----->
+ * ^plno
+ *
+ * All line numbers are 0-based.
+ */
+static void handle_split(struct blame_scoreboard *sb,
+ struct blame_entry *ent,
+ int tlno, int plno, int same,
+ struct blame_origin *parent,
+ struct blame_entry *split)
+{
+ if (ent->num_lines <= tlno)
+ return;
+ if (tlno < same) {
+ struct blame_entry this[3];
+ tlno += ent->s_lno;
+ same += ent->s_lno;
+ split_overlap(this, ent, tlno, plno, same, parent);
+ copy_split_if_better(sb, split, this);
+ decref_split(this);
+ }
+}
+
+struct handle_split_cb_data {
+ struct blame_scoreboard *sb;
+ struct blame_entry *ent;
+ struct blame_origin *parent;
+ struct blame_entry *split;
+ long plno;
+ long tlno;
+};
+
+static int handle_split_cb(long start_a, long count_a,
+ long start_b, long count_b, void *data)
+{
+ struct handle_split_cb_data *d = data;
+ handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
+ d->split);
+ d->plno = start_a + count_a;
+ d->tlno = start_b + count_b;
+ return 0;
+}
+
+/*
+ * Find the lines from parent that are the same as ent so that
+ * we can pass blames to it. file_p has the blob contents for
+ * the parent.
+ */
+static void find_copy_in_blob(struct blame_scoreboard *sb,
+ struct blame_entry *ent,
+ struct blame_origin *parent,
+ struct blame_entry *split,
+ mmfile_t *file_p)
+{
+ const char *cp;
+ mmfile_t file_o;
+ struct handle_split_cb_data d;
+
+ memset(&d, 0, sizeof(d));
+ d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
+ /*
+ * Prepare mmfile that contains only the lines in ent.
+ */
+ cp = blame_nth_line(sb, ent->lno);
+ file_o.ptr = (char *) cp;
+ file_o.size = blame_nth_line(sb, ent->lno + ent->num_lines) - cp;
+
+ /*
+ * file_o is a part of final image we are annotating.
+ * file_p partially may match that image.
+ */
+ memset(split, 0, sizeof(struct blame_entry [3]));
+ if (diff_hunks(file_p, &file_o, handle_split_cb, &d, sb->xdl_opts))
+ die("unable to generate diff (%s)",
+ oid_to_hex(&parent->commit->object.oid));
+ /* remainder, if any, all match the preimage */
+ handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
+}
+
+/* Move all blame entries from list *source that have a score smaller
+ * than score_min to the front of list *small.
+ * Returns a pointer to the link pointing to the old head of the small list.
+ */
+
+static struct blame_entry **filter_small(struct blame_scoreboard *sb,
+ struct blame_entry **small,
+ struct blame_entry **source,
+ unsigned score_min)
+{
+ struct blame_entry *p = *source;
+ struct blame_entry *oldsmall = *small;
+ while (p) {
+ if (blame_entry_score(sb, p) <= score_min) {
+ *small = p;
+ small = &p->next;
+ p = *small;
+ } else {
+ *source = p;
+ source = &p->next;
+ p = *source;
+ }
+ }
+ *small = oldsmall;
+ *source = NULL;
+ return small;
+}
+
+/*
+ * See if lines currently target is suspected for can be attributed to
+ * parent.
+ */
+static void find_move_in_parent(struct blame_scoreboard *sb,
+ struct blame_entry ***blamed,
+ struct blame_entry **toosmall,
+ struct blame_origin *target,
+ struct blame_origin *parent)
+{
+ struct blame_entry *e, split[3];
+ struct blame_entry *unblamed = target->suspects;
+ struct blame_entry *leftover = NULL;
+ mmfile_t file_p;
+
+ if (!unblamed)
+ return; /* nothing remains for this target */
+
+ fill_origin_blob(&sb->revs->diffopt, parent, &file_p, &sb->num_read_blob);
+ if (!file_p.ptr)
+ return;
+
+ /* At each iteration, unblamed has a NULL-terminated list of
+ * entries that have not yet been tested for blame. leftover
+ * contains the reversed list of entries that have been tested
+ * without being assignable to the parent.
+ */
+ do {
+ struct blame_entry **unblamedtail = &unblamed;
+ struct blame_entry *next;
+ for (e = unblamed; e; e = next) {
+ next = e->next;
+ find_copy_in_blob(sb, e, parent, split, &file_p);
+ if (split[1].suspect &&
+ sb->move_score < blame_entry_score(sb, &split[1])) {
+ split_blame(blamed, &unblamedtail, split, e);
+ } else {
+ e->next = leftover;
+ leftover = e;
+ }
+ decref_split(split);
+ }
+ *unblamedtail = NULL;
+ toosmall = filter_small(sb, toosmall, &unblamed, sb->move_score);
+ } while (unblamed);
+ target->suspects = reverse_blame(leftover, NULL);
+}
+
+struct blame_list {
+ struct blame_entry *ent;
+ struct blame_entry split[3];
+};
+
+/*
+ * Count the number of entries the target is suspected for,
+ * and prepare a list of entry and the best split.
+ */
+static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
+ int *num_ents_p)
+{
+ struct blame_entry *e;
+ int num_ents, i;
+ struct blame_list *blame_list = NULL;
+
+ for (e = unblamed, num_ents = 0; e; e = e->next)
+ num_ents++;
+ if (num_ents) {
+ blame_list = xcalloc(num_ents, sizeof(struct blame_list));
+ for (e = unblamed, i = 0; e; e = e->next)
+ blame_list[i++].ent = e;
+ }
+ *num_ents_p = num_ents;
+ return blame_list;
+}
+
+/*
+ * For lines target is suspected for, see if we can find code movement
+ * across file boundary from the parent commit. porigin is the path
+ * in the parent we already tried.
+ */
+static void find_copy_in_parent(struct blame_scoreboard *sb,
+ struct blame_entry ***blamed,
+ struct blame_entry **toosmall,
+ struct blame_origin *target,
+ struct commit *parent,
+ struct blame_origin *porigin,
+ int opt)
+{
+ struct diff_options diff_opts;
+ int i, j;
+ struct blame_list *blame_list;
+ int num_ents;
+ struct blame_entry *unblamed = target->suspects;
+ struct blame_entry *leftover = NULL;
+
+ if (!unblamed)
+ return; /* nothing remains for this target */
+
+ diff_setup(&diff_opts);
+ DIFF_OPT_SET(&diff_opts, RECURSIVE);
+ diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
+
+ diff_setup_done(&diff_opts);
+
+ /* Try "find copies harder" on new path if requested;
+ * we do not want to use diffcore_rename() actually to
+ * match things up; find_copies_harder is set only to
+ * force diff_tree_sha1() to feed all filepairs to diff_queue,
+ * and this code needs to be after diff_setup_done(), which
+ * usually makes find-copies-harder imply copy detection.
+ */
+ if ((opt & PICKAXE_BLAME_COPY_HARDEST)
+ || ((opt & PICKAXE_BLAME_COPY_HARDER)
+ && (!porigin || strcmp(target->path, porigin->path))))
+ DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
+
+ if (is_null_oid(&target->commit->object.oid))
+ do_diff_cache(&parent->tree->object.oid, &diff_opts);
+ else
+ diff_tree_sha1(parent->tree->object.oid.hash,
+ target->commit->tree->object.oid.hash,
+ "", &diff_opts);
+
+ if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
+ diffcore_std(&diff_opts);
+
+ do {
+ struct blame_entry **unblamedtail = &unblamed;
+ blame_list = setup_blame_list(unblamed, &num_ents);
+
+ for (i = 0; i < diff_queued_diff.nr; i++) {
+ struct diff_filepair *p = diff_queued_diff.queue[i];
+ struct blame_origin *norigin;
+ mmfile_t file_p;
+ struct blame_entry this[3];
+
+ if (!DIFF_FILE_VALID(p->one))
+ continue; /* does not exist in parent */
+ if (S_ISGITLINK(p->one->mode))
+ continue; /* ignore git links */
+ if (porigin && !strcmp(p->one->path, porigin->path))
+ /* find_move already dealt with this path */
+ continue;
+
+ norigin = get_origin(parent, p->one->path);
+ oidcpy(&norigin->blob_oid, &p->one->oid);
+ norigin->mode = p->one->mode;
+ fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob);
+ if (!file_p.ptr)
+ continue;
+
+ for (j = 0; j < num_ents; j++) {
+ find_copy_in_blob(sb, blame_list[j].ent,
+ norigin, this, &file_p);
+ copy_split_if_better(sb, blame_list[j].split,
+ this);
+ decref_split(this);
+ }
+ blame_origin_decref(norigin);
+ }
+
+ for (j = 0; j < num_ents; j++) {
+ struct blame_entry *split = blame_list[j].split;
+ if (split[1].suspect &&
+ sb->copy_score < blame_entry_score(sb, &split[1])) {
+ split_blame(blamed, &unblamedtail, split,
+ blame_list[j].ent);
+ } else {
+ blame_list[j].ent->next = leftover;
+ leftover = blame_list[j].ent;
+ }
+ decref_split(split);
+ }
+ free(blame_list);
+ *unblamedtail = NULL;
+ toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score);
+ } while (unblamed);
+ target->suspects = reverse_blame(leftover, NULL);
+ diff_flush(&diff_opts);
+ clear_pathspec(&diff_opts.pathspec);
+}
+
+/*
+ * The blobs of origin and porigin exactly match, so everything
+ * origin is suspected for can be blamed on the parent.
+ */
+static void pass_whole_blame(struct blame_scoreboard *sb,
+ struct blame_origin *origin, struct blame_origin *porigin)
+{
+ struct blame_entry *e, *suspects;
+
+ if (!porigin->file.ptr && origin->file.ptr) {
+ /* Steal its file */
+ porigin->file = origin->file;
+ origin->file.ptr = NULL;
+ }
+ suspects = origin->suspects;
+ origin->suspects = NULL;
+ for (e = suspects; e; e = e->next) {
+ blame_origin_incref(porigin);
+ blame_origin_decref(e->suspect);
+ e->suspect = porigin;
+ }
+ queue_blames(sb, porigin, suspects);
+}
+
+/*
+ * We pass blame from the current commit to its parents. We keep saying
+ * "parent" (and "porigin"), but what we mean is to find scapegoat to
+ * exonerate ourselves.
+ */
+static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit,
+ int reverse)
+{
+ if (!reverse) {
+ if (revs->first_parent_only &&
+ commit->parents &&
+ commit->parents->next) {
+ free_commit_list(commit->parents->next);
+ commit->parents->next = NULL;
+ }
+ return commit->parents;
+ }
+ return lookup_decoration(&revs->children, &commit->object);
+}
+
+static int num_scapegoats(struct rev_info *revs, struct commit *commit, int reverse)
+{
+ struct commit_list *l = first_scapegoat(revs, commit, reverse);
+ return commit_list_count(l);
+}
+
+/* Distribute collected unsorted blames to the respected sorted lists
+ * in the various origins.
+ */
+static void distribute_blame(struct blame_scoreboard *sb, struct blame_entry *blamed)
+{
+ blamed = llist_mergesort(blamed, get_next_blame, set_next_blame,
+ compare_blame_suspect);
+ while (blamed)
+ {
+ struct blame_origin *porigin = blamed->suspect;
+ struct blame_entry *suspects = NULL;
+ do {
+ struct blame_entry *next = blamed->next;
+ blamed->next = suspects;
+ suspects = blamed;
+ blamed = next;
+ } while (blamed && blamed->suspect == porigin);
+ suspects = reverse_blame(suspects, NULL);
+ queue_blames(sb, porigin, suspects);
+ }
+}
+
+#define MAXSG 16
+
+static void pass_blame(struct blame_scoreboard *sb, struct blame_origin *origin, int opt)
+{
+ struct rev_info *revs = sb->revs;
+ int i, pass, num_sg;
+ struct commit *commit = origin->commit;
+ struct commit_list *sg;
+ struct blame_origin *sg_buf[MAXSG];
+ struct blame_origin *porigin, **sg_origin = sg_buf;
+ struct blame_entry *toosmall = NULL;
+ struct blame_entry *blames, **blametail = &blames;
+
+ num_sg = num_scapegoats(revs, commit, sb->reverse);
+ if (!num_sg)
+ goto finish;
+ else if (num_sg < ARRAY_SIZE(sg_buf))
+ memset(sg_buf, 0, sizeof(sg_buf));
+ else
+ sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
+
+ /*
+ * The first pass looks for unrenamed path to optimize for
+ * 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 *);
+ find = pass ? find_rename : find_origin;
+
+ for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
+ i < num_sg && sg;
+ sg = sg->next, i++) {
+ struct commit *p = sg->item;
+ int j, same;
+
+ if (sg_origin[i])
+ continue;
+ if (parse_commit(p))
+ continue;
+ porigin = find(p, origin);
+ if (!porigin)
+ continue;
+ if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
+ pass_whole_blame(sb, origin, porigin);
+ blame_origin_decref(porigin);
+ goto finish;
+ }
+ for (j = same = 0; j < i; j++)
+ if (sg_origin[j] &&
+ !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {
+ same = 1;
+ break;
+ }
+ if (!same)
+ sg_origin[i] = porigin;
+ else
+ blame_origin_decref(porigin);
+ }
+ }
+
+ sb->num_commits++;
+ for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
+ i < num_sg && sg;
+ sg = sg->next, i++) {
+ struct blame_origin *porigin = sg_origin[i];
+ if (!porigin)
+ continue;
+ if (!origin->previous) {
+ blame_origin_incref(porigin);
+ origin->previous = porigin;
+ }
+ pass_blame_to_parent(sb, origin, porigin);
+ if (!origin->suspects)
+ goto finish;
+ }
+
+ /*
+ * Optionally find moves in parents' files.
+ */
+ if (opt & PICKAXE_BLAME_MOVE) {
+ filter_small(sb, &toosmall, &origin->suspects, sb->move_score);
+ if (origin->suspects) {
+ for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
+ i < num_sg && sg;
+ sg = sg->next, i++) {
+ struct blame_origin *porigin = sg_origin[i];
+ if (!porigin)
+ continue;
+ find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
+ if (!origin->suspects)
+ break;
+ }
+ }
+ }
+
+ /*
+ * Optionally find copies from parents' files.
+ */
+ if (opt & PICKAXE_BLAME_COPY) {
+ if (sb->copy_score > sb->move_score)
+ filter_small(sb, &toosmall, &origin->suspects, sb->copy_score);
+ else if (sb->copy_score < sb->move_score) {
+ origin->suspects = blame_merge(origin->suspects, toosmall);
+ toosmall = NULL;
+ filter_small(sb, &toosmall, &origin->suspects, sb->copy_score);
+ }
+ if (!origin->suspects)
+ goto finish;
+
+ for (i = 0, sg = first_scapegoat(revs, commit, sb->reverse);
+ i < num_sg && sg;
+ sg = sg->next, i++) {
+ struct blame_origin *porigin = sg_origin[i];
+ find_copy_in_parent(sb, &blametail, &toosmall,
+ origin, sg->item, porigin, opt);
+ if (!origin->suspects)
+ goto finish;
+ }
+ }
+
+finish:
+ *blametail = NULL;
+ distribute_blame(sb, blames);
+ /*
+ * prepend toosmall to origin->suspects
+ *
+ * There is no point in sorting: this ends up on a big
+ * unsorted list in the caller anyway.
+ */
+ if (toosmall) {
+ struct blame_entry **tail = &toosmall;
+ while (*tail)
+ tail = &(*tail)->next;
+ *tail = origin->suspects;
+ origin->suspects = toosmall;
+ }
+ for (i = 0; i < num_sg; i++) {
+ if (sg_origin[i]) {
+ drop_origin_blob(sg_origin[i]);
+ blame_origin_decref(sg_origin[i]);
+ }
+ }
+ drop_origin_blob(origin);
+ if (sg_buf != sg_origin)
+ free(sg_origin);
+}
+
+/*
+ * The main loop -- while we have blobs with lines whose true origin
+ * is still unknown, pick one blob, and allow its lines to pass blames
+ * to its parents. */
+void assign_blame(struct blame_scoreboard *sb, int opt)
+{
+ struct rev_info *revs = sb->revs;
+ struct commit *commit = prio_queue_get(&sb->commits);
+
+ while (commit) {
+ struct blame_entry *ent;
+ struct blame_origin *suspect = commit->util;
+
+ /* find one suspect to break down */
+ while (suspect && !suspect->suspects)
+ suspect = suspect->next;
+
+ if (!suspect) {
+ commit = prio_queue_get(&sb->commits);
+ continue;
+ }
+
+ assert(commit == suspect->commit);
+
+ /*
+ * We will use this suspect later in the loop,
+ * so hold onto it in the meantime.
+ */
+ blame_origin_incref(suspect);
+ parse_commit(commit);
+ if (sb->reverse ||
+ (!(commit->object.flags & UNINTERESTING) &&
+ !(revs->max_age != -1 && commit->date < revs->max_age)))
+ pass_blame(sb, suspect, opt);
+ else {
+ commit->object.flags |= UNINTERESTING;
+ if (commit->object.parsed)
+ mark_parents_uninteresting(commit);
+ }
+ /* treat root commit as boundary */
+ if (!commit->parents && !sb->show_root)
+ commit->object.flags |= UNINTERESTING;
+
+ /* Take responsibility for the remaining entries */
+ ent = suspect->suspects;
+ if (ent) {
+ suspect->guilty = 1;
+ for (;;) {
+ struct blame_entry *next = ent->next;
+ if (sb->found_guilty_entry)
+ sb->found_guilty_entry(ent, sb->found_guilty_entry_data);
+ if (next) {
+ ent = next;
+ continue;
+ }
+ ent->next = sb->ent;
+ sb->ent = suspect->suspects;
+ suspect->suspects = NULL;
+ break;
+ }
+ }
+ blame_origin_decref(suspect);
+
+ if (sb->debug) /* sanity */
+ sanity_check_refcnt(sb);
+ }
+}
+
+static const char *get_next_line(const char *start, const char *end)
+{
+ const char *nl = memchr(start, '\n', end - start);
+ return nl ? nl + 1 : end;
+}
+
+/*
+ * To allow quick access to the contents of nth line in the
+ * final image, prepare an index in the scoreboard.
+ */
+static int prepare_lines(struct blame_scoreboard *sb)
+{
+ const char *buf = sb->final_buf;
+ unsigned long len = sb->final_buf_size;
+ const char *end = buf + len;
+ const char *p;
+ int *lineno;
+ int num = 0;
+
+ for (p = buf; p < end; p = get_next_line(p, end))
+ num++;
+
+ ALLOC_ARRAY(sb->lineno, num + 1);
+ lineno = sb->lineno;
+
+ for (p = buf; p < end; p = get_next_line(p, end))
+ *lineno++ = p - buf;
+
+ *lineno = len;
+
+ sb->num_lines = num;
+ return sb->num_lines;
+}
+
+static struct commit *find_single_final(struct rev_info *revs,
+ const char **name_p)
+{
+ int i;
+ struct commit *found = NULL;
+ const char *name = NULL;
+
+ for (i = 0; i < revs->pending.nr; i++) {
+ struct object *obj = revs->pending.objects[i].item;
+ if (obj->flags & UNINTERESTING)
+ continue;
+ obj = deref_tag(obj, NULL, 0);
+ if (obj->type != OBJ_COMMIT)
+ die("Non commit %s?", revs->pending.objects[i].name);
+ if (found)
+ die("More than one commit to dig from %s and %s?",
+ revs->pending.objects[i].name, name);
+ found = (struct commit *)obj;
+ name = revs->pending.objects[i].name;
+ }
+ if (name_p)
+ *name_p = name;
+ return found;
+}
+
+static struct commit *dwim_reverse_initial(struct rev_info *revs,
+ const char **name_p)
+{
+ /*
+ * DWIM "git blame --reverse ONE -- PATH" as
+ * "git blame --reverse ONE..HEAD -- PATH" but only do so
+ * when it makes sense.
+ */
+ struct object *obj;
+ struct commit *head_commit;
+ struct object_id head_oid;
+
+ if (revs->pending.nr != 1)
+ return NULL;
+
+ /* Is that sole rev a committish? */
+ obj = revs->pending.objects[0].item;
+ obj = deref_tag(obj, NULL, 0);
+ if (obj->type != OBJ_COMMIT)
+ return NULL;
+
+ /* Do we have HEAD? */
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
+ return NULL;
+ head_commit = lookup_commit_reference_gently(&head_oid, 1);
+ if (!head_commit)
+ return NULL;
+
+ /* Turn "ONE" into "ONE..HEAD" then */
+ obj->flags |= UNINTERESTING;
+ add_pending_object(revs, &head_commit->object, "HEAD");
+
+ if (name_p)
+ *name_p = revs->pending.objects[0].name;
+ return (struct commit *)obj;
+}
+
+static struct commit *find_single_initial(struct rev_info *revs,
+ const char **name_p)
+{
+ int i;
+ struct commit *found = NULL;
+ const char *name = NULL;
+
+ /*
+ * There must be one and only one negative commit, and it must be
+ * the boundary.
+ */
+ for (i = 0; i < revs->pending.nr; i++) {
+ struct object *obj = revs->pending.objects[i].item;
+ if (!(obj->flags & UNINTERESTING))
+ continue;
+ obj = deref_tag(obj, NULL, 0);
+ if (obj->type != OBJ_COMMIT)
+ die("Non commit %s?", revs->pending.objects[i].name);
+ if (found)
+ die("More than one commit to dig up from, %s and %s?",
+ revs->pending.objects[i].name, name);
+ found = (struct commit *) obj;
+ name = revs->pending.objects[i].name;
+ }
+
+ if (!name)
+ found = dwim_reverse_initial(revs, &name);
+ if (!name)
+ die("No commit to dig up from?");
+
+ if (name_p)
+ *name_p = name;
+ return found;
+}
+
+void init_scoreboard(struct blame_scoreboard *sb)
+{
+ memset(sb, 0, sizeof(struct blame_scoreboard));
+ sb->move_score = BLAME_DEFAULT_MOVE_SCORE;
+ sb->copy_score = BLAME_DEFAULT_COPY_SCORE;
+}
+
+void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig)
+{
+ const char *final_commit_name = NULL;
+ struct blame_origin *o;
+ struct commit *final_commit = NULL;
+ enum object_type type;
+
+ if (sb->reverse && sb->contents_from)
+ die(_("--contents and --reverse do not blend well."));
+
+ if (!sb->reverse) {
+ sb->final = find_single_final(sb->revs, &final_commit_name);
+ sb->commits.compare = compare_commits_by_commit_date;
+ } else {
+ sb->final = find_single_initial(sb->revs, &final_commit_name);
+ sb->commits.compare = compare_commits_by_reverse_commit_date;
+ }
+
+ if (sb->final && sb->contents_from)
+ die(_("cannot use --contents with final commit object name"));
+
+ if (sb->reverse && sb->revs->first_parent_only)
+ sb->revs->children.name = NULL;
+
+ if (!sb->final) {
+ /*
+ * "--not A B -- path" without anything positive;
+ * do not default to HEAD, but use the working tree
+ * or "--contents".
+ */
+ setup_work_tree();
+ sb->final = fake_working_tree_commit(&sb->revs->diffopt,
+ path, sb->contents_from);
+ add_pending_object(sb->revs, &(sb->final->object), ":");
+ }
+
+ if (sb->reverse && sb->revs->first_parent_only) {
+ final_commit = find_single_final(sb->revs, NULL);
+ if (!final_commit)
+ die(_("--reverse and --first-parent together require specified latest commit"));
+ }
+
+ /*
+ * If we have bottom, this will mark the ancestors of the
+ * bottom commits we would reach while traversing as
+ * uninteresting.
+ */
+ if (prepare_revision_walk(sb->revs))
+ die(_("revision walk setup failed"));
+
+ if (sb->reverse && sb->revs->first_parent_only) {
+ struct commit *c = final_commit;
+
+ sb->revs->children.name = "children";
+ while (c->parents &&
+ oidcmp(&c->object.oid, &sb->final->object.oid)) {
+ struct commit_list *l = xcalloc(1, sizeof(*l));
+
+ l->item = c;
+ if (add_decoration(&sb->revs->children,
+ &c->parents->item->object, l))
+ die("BUG: not unique item in first-parent chain");
+ c = c->parents->item;
+ }
+
+ if (oidcmp(&c->object.oid, &sb->final->object.oid))
+ die(_("--reverse --first-parent together require range along first-parent chain"));
+ }
+
+ if (is_null_oid(&sb->final->object.oid)) {
+ o = sb->final->util;
+ sb->final_buf = xmemdupz(o->file.ptr, o->file.size);
+ sb->final_buf_size = o->file.size;
+ }
+ else {
+ o = get_origin(sb->final, path);
+ if (fill_blob_sha1_and_mode(o))
+ die(_("no such path %s in %s"), path, final_commit_name);
+
+ if (DIFF_OPT_TST(&sb->revs->diffopt, ALLOW_TEXTCONV) &&
+ textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb->final_buf,
+ &sb->final_buf_size))
+ ;
+ else
+ sb->final_buf = read_sha1_file(o->blob_oid.hash, &type,
+ &sb->final_buf_size);
+
+ if (!sb->final_buf)
+ die(_("cannot read blob %s for path %s"),
+ oid_to_hex(&o->blob_oid),
+ path);
+ }
+ sb->num_read_blob++;
+ prepare_lines(sb);
+
+ if (orig)
+ *orig = o;
+}
+
+
+
+struct blame_entry *blame_entry_prepend(struct blame_entry *head,
+ long start, long end,
+ struct blame_origin *o)
+{
+ struct blame_entry *new_head = xcalloc(1, sizeof(struct blame_entry));
+ new_head->lno = start;
+ new_head->num_lines = end - start;
+ new_head->suspect = o;
+ new_head->s_lno = start;
+ new_head->next = head;
+ blame_origin_incref(o);
+ return new_head;
+}
diff --git a/blame.h b/blame.h
new file mode 100644
index 0000000000..a6c915c277
--- /dev/null
+++ b/blame.h
@@ -0,0 +1,175 @@
+#ifndef BLAME_H
+#define BLAME_H
+
+#include "cache.h"
+#include "commit.h"
+#include "xdiff-interface.h"
+#include "revision.h"
+#include "prio-queue.h"
+#include "diff.h"
+
+#define PICKAXE_BLAME_MOVE 01
+#define PICKAXE_BLAME_COPY 02
+#define PICKAXE_BLAME_COPY_HARDER 04
+#define PICKAXE_BLAME_COPY_HARDEST 010
+
+#define BLAME_DEFAULT_MOVE_SCORE 20
+#define BLAME_DEFAULT_COPY_SCORE 40
+
+/*
+ * One blob in a commit that is being suspected
+ */
+struct blame_origin {
+ int refcnt;
+ /* Record preceding blame record for this blob */
+ struct blame_origin *previous;
+ /* origins are put in a list linked via `next' hanging off the
+ * corresponding commit's util field in order to make finding
+ * them fast. The presence in this chain does not count
+ * towards the origin's reference count. It is tempting to
+ * let it count as long as the commit is pending examination,
+ * but even under circumstances where the commit will be
+ * present multiple times in the priority queue of unexamined
+ * commits, processing the first instance will not leave any
+ * work requiring the origin data for the second instance. An
+ * interspersed commit changing that would have to be
+ * preexisting with a different ancestry and with the same
+ * commit date in order to wedge itself between two instances
+ * of the same commit in the priority queue _and_ produce
+ * blame entries relevant for it. While we don't want to let
+ * us get tripped up by this case, it certainly does not seem
+ * worth optimizing for.
+ */
+ struct blame_origin *next;
+ struct commit *commit;
+ /* `suspects' contains blame entries that may be attributed to
+ * this origin's commit or to parent commits. When a commit
+ * is being processed, all suspects will be moved, either by
+ * assigning them to an origin in a different commit, or by
+ * shipping them to the scoreboard's ent list because they
+ * cannot be attributed to a different commit.
+ */
+ struct blame_entry *suspects;
+ mmfile_t file;
+ struct object_id blob_oid;
+ unsigned mode;
+ /* guilty gets set when shipping any suspects to the final
+ * blame list instead of other commits
+ */
+ char guilty;
+ char path[FLEX_ARRAY];
+};
+
+/*
+ * Each group of lines is described by a blame_entry; it can be split
+ * as we pass blame to the parents. They are arranged in linked lists
+ * kept as `suspects' of some unprocessed origin, or entered (when the
+ * blame origin has been finalized) into the scoreboard structure.
+ * While the scoreboard structure is only sorted at the end of
+ * processing (according to final image line number), the lists
+ * attached to an origin are sorted by the target line number.
+ */
+struct blame_entry {
+ struct blame_entry *next;
+
+ /* the first line of this group in the final image;
+ * internally all line numbers are 0 based.
+ */
+ int lno;
+
+ /* how many lines this group has */
+ int num_lines;
+
+ /* the commit that introduced this group into the final image */
+ struct blame_origin *suspect;
+
+ /* the line number of the first line of this group in the
+ * suspect's file; internally all line numbers are 0 based.
+ */
+ int s_lno;
+
+ /* how significant this entry is -- cached to avoid
+ * scanning the lines over and over.
+ */
+ unsigned score;
+};
+
+/*
+ * The current state of the blame assignment.
+ */
+struct blame_scoreboard {
+ /* the final commit (i.e. where we started digging from) */
+ struct commit *final;
+ /* Priority queue for commits with unassigned blame records */
+ struct prio_queue commits;
+ struct rev_info *revs;
+ const char *path;
+
+ /*
+ * The contents in the final image.
+ * Used by many functions to obtain contents of the nth line,
+ * indexed with scoreboard.lineno[blame_entry.lno].
+ */
+ const char *final_buf;
+ unsigned long final_buf_size;
+
+ /* linked list of blames */
+ struct blame_entry *ent;
+
+ /* look-up a line in the final buffer */
+ int num_lines;
+ int *lineno;
+
+ /* stats */
+ int num_read_blob;
+ int num_get_patch;
+ int num_commits;
+
+ /*
+ * blame for a blame_entry with score lower than these thresholds
+ * is not passed to the parent using move/copy logic.
+ */
+ unsigned move_score;
+ unsigned copy_score;
+
+ /* use this file's contents as the final image */
+ const char *contents_from;
+
+ /* flags */
+ int reverse;
+ int show_root;
+ int xdl_opts;
+ int no_whole_file_rename;
+ int debug;
+
+ /* callbacks */
+ void(*on_sanity_fail)(struct blame_scoreboard *, int);
+ void(*found_guilty_entry)(struct blame_entry *, void *);
+
+ void *found_guilty_entry_data;
+};
+
+/*
+ * Origin is refcounted and usually we keep the blob contents to be
+ * reused.
+ */
+static inline struct blame_origin *blame_origin_incref(struct blame_origin *o)
+{
+ if (o)
+ o->refcnt++;
+ return o;
+}
+extern void blame_origin_decref(struct blame_origin *o);
+
+extern void blame_coalesce(struct blame_scoreboard *sb);
+extern void blame_sort_final(struct blame_scoreboard *sb);
+extern unsigned blame_entry_score(struct blame_scoreboard *sb, struct blame_entry *e);
+extern void assign_blame(struct blame_scoreboard *sb, int opt);
+extern const char *blame_nth_line(struct blame_scoreboard *sb, long lno);
+
+extern void init_scoreboard(struct blame_scoreboard *sb);
+extern void setup_scoreboard(struct blame_scoreboard *sb, const char *path, struct blame_origin **orig);
+
+extern struct blame_entry *blame_entry_prepend(struct blame_entry *head, long start, long end, struct blame_origin *o);
+
+#endif /* BLAME_H */
diff --git a/blob.c b/blob.c
index 1fcb8e44b0..fa2ab4f7a7 100644
--- a/blob.c
+++ b/blob.c
@@ -3,11 +3,11 @@
const char *blob_type = "blob";
-struct blob *lookup_blob(const unsigned char *sha1)
+struct blob *lookup_blob(const struct object_id *oid)
{
- struct object *obj = lookup_object(sha1);
+ struct object *obj = lookup_object(oid->hash);
if (!obj)
- return create_object(sha1, alloc_blob_node());
+ return create_object(oid->hash, alloc_blob_node());
return object_as_type(obj, OBJ_BLOB, 0);
}
diff --git a/blob.h b/blob.h
index 59b394eea3..4460616831 100644
--- a/blob.h
+++ b/blob.h
@@ -9,7 +9,7 @@ struct blob {
struct object object;
};
-struct blob *lookup_blob(const unsigned char *sha1);
+struct blob *lookup_blob(const struct object_id *oid);
int parse_blob_buffer(struct blob *item, void *buffer, unsigned long size);
diff --git a/branch.c b/branch.c
index ad5a2299ba..985316eb76 100644
--- a/branch.c
+++ b/branch.c
@@ -191,9 +191,9 @@ int validate_new_branchname(const char *name, struct strbuf *ref,
if (!attr_only) {
const char *head;
- unsigned char sha1[20];
+ struct object_id oid;
- head = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
+ head = resolve_ref_unsafe("HEAD", 0, oid.hash, NULL);
if (!is_bare_repository() && head && !strcmp(head, ref->buf))
die(_("Cannot force update the current branch."));
}
@@ -233,7 +233,7 @@ void create_branch(const char *name, const char *start_name,
int quiet, enum branch_track track)
{
struct commit *commit;
- unsigned char sha1[20];
+ struct object_id oid;
char *real_ref;
struct strbuf ref = STRBUF_INIT;
int forcing = 0;
@@ -253,7 +253,7 @@ void create_branch(const char *name, const char *start_name,
}
real_ref = NULL;
- if (get_sha1(start_name, sha1)) {
+ if (get_oid(start_name, &oid)) {
if (explicit_tracking) {
if (advice_set_upstream_failure) {
error(_(upstream_missing), start_name);
@@ -265,7 +265,7 @@ void create_branch(const char *name, const char *start_name,
die(_("Not a valid object name: '%s'."), start_name);
}
- switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
+ switch (dwim_ref(start_name, strlen(start_name), oid.hash, &real_ref)) {
case 0:
/* Not branching from any existing branch */
if (explicit_tracking)
@@ -286,9 +286,9 @@ void create_branch(const char *name, const char *start_name,
break;
}
- if ((commit = lookup_commit_reference(sha1)) == NULL)
+ if ((commit = lookup_commit_reference(&oid)) == NULL)
die(_("Not a valid branch point: '%s'."), start_name);
- hashcpy(sha1, commit->object.oid.hash);
+ oidcpy(&oid, &commit->object.oid);
if (reflog)
log_all_ref_updates = LOG_REFS_NORMAL;
@@ -306,7 +306,7 @@ void create_branch(const char *name, const char *start_name,
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, ref.buf,
- sha1, forcing ? NULL : null_sha1,
+ oid.hash, forcing ? NULL : null_sha1,
0, msg, &err) ||
ref_transaction_commit(transaction, &err))
die("%s", err.buf);
@@ -353,17 +353,18 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref,
int i;
for (i = 0; worktrees[i]; i++) {
+ struct ref_store *refs;
+
if (worktrees[i]->is_detached)
continue;
- if (strcmp(oldref, worktrees[i]->head_ref))
+ if (worktrees[i]->head_ref &&
+ strcmp(oldref, worktrees[i]->head_ref))
continue;
- if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
- newref, logmsg)) {
- ret = -1;
- error(_("HEAD of working tree %s is not updated"),
- worktrees[i]->path);
- }
+ refs = get_worktree_ref_store(worktrees[i]);
+ if (refs_create_symref(refs, "HEAD", newref, logmsg))
+ ret = error(_("HEAD of working tree %s is not updated"),
+ worktrees[i]->path);
}
free_worktrees(worktrees);
diff --git a/builtin.h b/builtin.h
index 9e4a89816d..498ac80d07 100644
--- a/builtin.h
+++ b/builtin.h
@@ -25,8 +25,6 @@ struct fmt_merge_msg_opts {
extern int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
struct fmt_merge_msg_opts *);
-extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);
-
extern int is_builtin(const char *s);
extern int cmd_add(int argc, const char **argv, const char *prefix);
diff --git a/builtin/add.c b/builtin/add.c
index 9f53f020d0..d9a2491e48 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -17,6 +17,7 @@
#include "revision.h"
#include "bulk-checkin.h"
#include "argv-array.h"
+#include "submodule.h"
static const char * const builtin_add_usage[] = {
N_("git add [<options>] [--] <pathspec>..."),
@@ -135,7 +136,7 @@ static char *prune_directory(struct dir_struct *dir, struct pathspec *pathspec,
*dst++ = entry;
}
dir->nr = dst - dir->entries;
- add_pathspec_matches_against_index(pathspec, seen);
+ add_pathspec_matches_against_index(pathspec, &the_index, seen);
return seen;
}
@@ -379,16 +380,19 @@ int cmd_add(int argc, const char **argv, const char *prefix)
if (read_cache() < 0)
die(_("index file corrupt"));
+ die_in_unpopulated_submodule(&the_index, prefix);
+
/*
* Check the "pathspec '%s' did not match any files" block
* below before enabling new magic.
*/
parse_pathspec(&pathspec, 0,
PATHSPEC_PREFER_FULL |
- PATHSPEC_SYMLINK_LEADING_PATH |
- PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE,
+ PATHSPEC_SYMLINK_LEADING_PATH,
prefix, argv);
+ die_path_inside_submodule(&the_index, &pathspec);
+
if (add_new_files) {
int baselen;
@@ -400,7 +404,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
}
/* This picks up the paths that are not tracked */
- baselen = fill_directory(&dir, &pathspec);
+ baselen = fill_directory(&dir, &the_index, &pathspec);
if (pathspec.nr)
seen = prune_directory(&dir, &pathspec, baselen);
}
@@ -414,7 +418,7 @@ int cmd_add(int argc, const char **argv, const char *prefix)
int i;
if (!seen)
- seen = find_pathspecs_matching_against_index(&pathspec);
+ seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
/*
* file_exists() assumes exact match
@@ -436,8 +440,9 @@ int cmd_add(int argc, const char **argv, const char *prefix)
!file_exists(path))) {
if (ignore_missing) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, path, &dtype))
- dir_add_ignored(&dir, path, pathspec.items[i].len);
+ if (is_excluded(&dir, &the_index, path, &dtype))
+ dir_add_ignored(&dir, &the_index,
+ path, pathspec.items[i].len);
} else
die(_("pathspec '%s' did not match any files"),
pathspec.items[i].original);
diff --git a/builtin/am.c b/builtin/am.c
index 593c1b5dda..5ee146bfb3 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -879,12 +879,12 @@ static int hg_patch_to_mail(FILE *out, FILE *in, int keep_cr)
if (skip_prefix(sb.buf, "# User ", &str))
fprintf(out, "From: %s\n", str);
else if (skip_prefix(sb.buf, "# Date ", &str)) {
- unsigned long timestamp;
+ timestamp_t timestamp;
long tz, tz2;
char *end;
errno = 0;
- timestamp = strtoul(str, &end, 10);
+ timestamp = parse_timestamp(str, &end, 10);
if (errno)
return error(_("invalid timestamp"));
@@ -1145,7 +1145,7 @@ static int index_has_changes(struct strbuf *sb)
DIFF_OPT_SET(&opt, EXIT_WITH_STATUS);
if (!sb)
DIFF_OPT_SET(&opt, QUICK);
- do_diff_cache(head.hash, &opt);
+ do_diff_cache(&head, &opt);
diffcore_std(&opt);
for (i = 0; sb && i < diff_queued_diff.nr; i++) {
if (i)
@@ -1444,9 +1444,9 @@ static void write_index_patch(const struct am_state *state)
FILE *fp;
if (!get_sha1_tree("HEAD", head.hash))
- tree = lookup_tree(head.hash);
+ tree = lookup_tree(&head);
else
- tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+ tree = lookup_tree(&empty_tree_oid);
fp = xfopen(am_path(state, "patch"), "w");
init_revisions(&rev_info, NULL);
@@ -1479,7 +1479,7 @@ static int parse_mail_rebase(struct am_state *state, const char *mail)
if (get_mail_commit_oid(&commit_oid, mail) < 0)
die(_("could not parse %s"), mail);
- commit = lookup_commit_or_die(commit_oid.hash, mail);
+ commit = lookup_commit_or_die(&commit_oid, mail);
get_commit_info(state, commit);
@@ -1609,7 +1609,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
init_revisions(&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_sha1(&rev_info, "HEAD", our_tree.hash, 0);
+ add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
diff_setup_done(&rev_info.diffopt);
run_diff_index(&rev_info, 1);
}
@@ -1674,7 +1674,7 @@ static void do_commit(const struct am_state *state)
if (!get_sha1_commit("HEAD", parent.hash)) {
old_oid = &parent;
- commit_list_insert(lookup_commit(parent.hash), &parents);
+ commit_list_insert(lookup_commit(&parent), &parents);
} else {
old_oid = NULL;
say(state, stderr, _("applying to an empty history"));
@@ -2037,11 +2037,11 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
struct tree *head_tree, *remote_tree, *index_tree;
struct object_id index;
- head_tree = parse_tree_indirect(head->hash);
+ head_tree = parse_tree_indirect(head);
if (!head_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(head));
- remote_tree = parse_tree_indirect(remote->hash);
+ remote_tree = parse_tree_indirect(remote);
if (!remote_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(remote));
@@ -2053,7 +2053,7 @@ static int clean_index(const struct object_id *head, const struct object_id *rem
if (write_cache_as_tree(index.hash, 0, NULL))
return -1;
- index_tree = parse_tree_indirect(index.hash);
+ index_tree = parse_tree_indirect(&index);
if (!index_tree)
return error(_("Could not parse object '%s'."), oid_to_hex(&index));
diff --git a/builtin/blame.c b/builtin/blame.c
index ca9ebe40e7..d7a2df3b47 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -6,21 +6,13 @@
*/
#include "cache.h"
-#include "refs.h"
#include "builtin.h"
-#include "blob.h"
#include "commit.h"
-#include "tag.h"
-#include "tree-walk.h"
#include "diff.h"
-#include "diffcore.h"
#include "revision.h"
#include "quote.h"
-#include "xdiff-interface.h"
-#include "cache-tree.h"
#include "string-list.h"
#include "mailmap.h"
-#include "mergesort.h"
#include "parse-options.h"
#include "prio-queue.h"
#include "utf8.h"
@@ -29,6 +21,7 @@
#include "line-log.h"
#include "dir.h"
#include "progress.h"
+#include "blame.h"
static char blame_usage[] = N_("git blame [<options>] [<rev-opts>] [<rev>] [--] <file>");
@@ -62,1497 +55,21 @@ static struct string_list mailmap = STRING_LIST_INIT_NODUP;
#define DEBUG 0
#endif
-/* stats */
-static int num_read_blob;
-static int num_get_patch;
-static int num_commits;
-
-#define PICKAXE_BLAME_MOVE 01
-#define PICKAXE_BLAME_COPY 02
-#define PICKAXE_BLAME_COPY_HARDER 04
-#define PICKAXE_BLAME_COPY_HARDEST 010
-
-/*
- * blame for a blame_entry with score lower than these thresholds
- * is not passed to the parent using move/copy logic.
- */
static unsigned blame_move_score;
static unsigned blame_copy_score;
-#define BLAME_DEFAULT_MOVE_SCORE 20
-#define BLAME_DEFAULT_COPY_SCORE 40
/* Remember to update object flag allocation in object.h */
#define METAINFO_SHOWN (1u<<12)
#define MORE_THAN_ONE_PATH (1u<<13)
-/*
- * One blob in a commit that is being suspected
- */
-struct origin {
- int refcnt;
- /* Record preceding blame record for this blob */
- struct origin *previous;
- /* origins are put in a list linked via `next' hanging off the
- * corresponding commit's util field in order to make finding
- * them fast. The presence in this chain does not count
- * towards the origin's reference count. It is tempting to
- * let it count as long as the commit is pending examination,
- * but even under circumstances where the commit will be
- * present multiple times in the priority queue of unexamined
- * commits, processing the first instance will not leave any
- * work requiring the origin data for the second instance. An
- * interspersed commit changing that would have to be
- * preexisting with a different ancestry and with the same
- * commit date in order to wedge itself between two instances
- * of the same commit in the priority queue _and_ produce
- * blame entries relevant for it. While we don't want to let
- * us get tripped up by this case, it certainly does not seem
- * worth optimizing for.
- */
- struct origin *next;
- struct commit *commit;
- /* `suspects' contains blame entries that may be attributed to
- * this origin's commit or to parent commits. When a commit
- * is being processed, all suspects will be moved, either by
- * assigning them to an origin in a different commit, or by
- * shipping them to the scoreboard's ent list because they
- * cannot be attributed to a different commit.
- */
- struct blame_entry *suspects;
- mmfile_t file;
- struct object_id blob_oid;
- unsigned mode;
- /* guilty gets set when shipping any suspects to the final
- * blame list instead of other commits
- */
- char guilty;
- char path[FLEX_ARRAY];
-};
-
struct progress_info {
struct progress *progress;
int blamed_lines;
};
-static int diff_hunks(mmfile_t *file_a, mmfile_t *file_b,
- xdl_emit_hunk_consume_func_t hunk_func, void *cb_data)
-{
- xpparam_t xpp = {0};
- xdemitconf_t xecfg = {0};
- xdemitcb_t ecb = {NULL};
-
- xpp.flags = xdl_opts;
- xecfg.hunk_func = hunk_func;
- ecb.priv = cb_data;
- return xdi_diff(file_a, file_b, &xpp, &xecfg, &ecb);
-}
-
-/*
- * 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)
-{
- struct diff_filespec *df;
- struct userdiff_driver *textconv;
-
- df = alloc_filespec(path);
- fill_filespec(df, oid->hash, oid_valid, mode);
- textconv = get_textconv(df);
- if (!textconv) {
- free_filespec(df);
- return 0;
- }
-
- *buf_size = fill_textconv(textconv, df, buf);
- free_filespec(df);
- return 1;
-}
-
-/*
- * Given an origin, prepare mmfile_t structure to be used by the
- * diff machinery
- */
-static void fill_origin_blob(struct diff_options *opt,
- struct origin *o, mmfile_t *file)
-{
- if (!o->file.ptr) {
- enum object_type type;
- unsigned long file_size;
-
- num_read_blob++;
- if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(o->path, o->mode, &o->blob_oid, 1, &file->ptr, &file_size))
- ;
- else
- file->ptr = read_sha1_file(o->blob_oid.hash, &type,
- &file_size);
- file->size = file_size;
-
- if (!file->ptr)
- die("Cannot read blob %s for path %s",
- oid_to_hex(&o->blob_oid),
- o->path);
- o->file = *file;
- }
- else
- *file = o->file;
-}
-
-/*
- * Origin is refcounted and usually we keep the blob contents to be
- * reused.
- */
-static inline struct origin *origin_incref(struct origin *o)
-{
- if (o)
- o->refcnt++;
- return o;
-}
-
-static void origin_decref(struct origin *o)
-{
- if (o && --o->refcnt <= 0) {
- struct origin *p, *l = NULL;
- if (o->previous)
- origin_decref(o->previous);
- free(o->file.ptr);
- /* Should be present exactly once in commit chain */
- for (p = o->commit->util; p; l = p, p = p->next) {
- if (p == o) {
- if (l)
- l->next = p->next;
- else
- o->commit->util = p->next;
- free(o);
- return;
- }
- }
- die("internal error in blame::origin_decref");
- }
-}
-
-static void drop_origin_blob(struct origin *o)
-{
- if (o->file.ptr) {
- free(o->file.ptr);
- o->file.ptr = NULL;
- }
-}
-
-/*
- * Each group of lines is described by a blame_entry; it can be split
- * as we pass blame to the parents. They are arranged in linked lists
- * kept as `suspects' of some unprocessed origin, or entered (when the
- * blame origin has been finalized) into the scoreboard structure.
- * While the scoreboard structure is only sorted at the end of
- * processing (according to final image line number), the lists
- * attached to an origin are sorted by the target line number.
- */
-struct blame_entry {
- struct blame_entry *next;
-
- /* the first line of this group in the final image;
- * internally all line numbers are 0 based.
- */
- int lno;
-
- /* how many lines this group has */
- int num_lines;
-
- /* the commit that introduced this group into the final image */
- struct origin *suspect;
-
- /* the line number of the first line of this group in the
- * suspect's file; internally all line numbers are 0 based.
- */
- int s_lno;
-
- /* how significant this entry is -- cached to avoid
- * scanning the lines over and over.
- */
- unsigned score;
-};
-
-/*
- * Any merge of blames happens on lists of blames that arrived via
- * different parents in a single suspect. In this case, we want to
- * sort according to the suspect line numbers as opposed to the final
- * image line numbers. The function body is somewhat longish because
- * it avoids unnecessary writes.
- */
-
-static struct blame_entry *blame_merge(struct blame_entry *list1,
- struct blame_entry *list2)
-{
- struct blame_entry *p1 = list1, *p2 = list2,
- **tail = &list1;
-
- if (!p1)
- return p2;
- if (!p2)
- return p1;
-
- if (p1->s_lno <= p2->s_lno) {
- do {
- tail = &p1->next;
- if ((p1 = *tail) == NULL) {
- *tail = p2;
- return list1;
- }
- } while (p1->s_lno <= p2->s_lno);
- }
- for (;;) {
- *tail = p2;
- do {
- tail = &p2->next;
- if ((p2 = *tail) == NULL) {
- *tail = p1;
- return list1;
- }
- } while (p1->s_lno > p2->s_lno);
- *tail = p1;
- do {
- tail = &p1->next;
- if ((p1 = *tail) == NULL) {
- *tail = p2;
- return list1;
- }
- } while (p1->s_lno <= p2->s_lno);
- }
-}
-
-static void *get_next_blame(const void *p)
-{
- return ((struct blame_entry *)p)->next;
-}
-
-static void set_next_blame(void *p1, void *p2)
-{
- ((struct blame_entry *)p1)->next = p2;
-}
-
-/*
- * Final image line numbers are all different, so we don't need a
- * three-way comparison here.
- */
-
-static int compare_blame_final(const void *p1, const void *p2)
-{
- return ((struct blame_entry *)p1)->lno > ((struct blame_entry *)p2)->lno
- ? 1 : -1;
-}
-
-static int compare_blame_suspect(const void *p1, const void *p2)
-{
- const struct blame_entry *s1 = p1, *s2 = p2;
- /*
- * to allow for collating suspects, we sort according to the
- * respective pointer value as the primary sorting criterion.
- * The actual relation is pretty unimportant as long as it
- * establishes a total order. Comparing as integers gives us
- * that.
- */
- if (s1->suspect != s2->suspect)
- return (intptr_t)s1->suspect > (intptr_t)s2->suspect ? 1 : -1;
- if (s1->s_lno == s2->s_lno)
- return 0;
- return s1->s_lno > s2->s_lno ? 1 : -1;
-}
-
-static struct blame_entry *blame_sort(struct blame_entry *head,
- int (*compare_fn)(const void *, const void *))
-{
- return llist_mergesort (head, get_next_blame, set_next_blame, compare_fn);
-}
-
-static int compare_commits_by_reverse_commit_date(const void *a,
- const void *b,
- void *c)
-{
- return -compare_commits_by_commit_date(a, b, c);
-}
-
-/*
- * The current state of the blame assignment.
- */
-struct scoreboard {
- /* the final commit (i.e. where we started digging from) */
- struct commit *final;
- /* Priority queue for commits with unassigned blame records */
- struct prio_queue commits;
- struct rev_info *revs;
- const char *path;
-
- /*
- * The contents in the final image.
- * Used by many functions to obtain contents of the nth line,
- * indexed with scoreboard.lineno[blame_entry.lno].
- */
- const char *final_buf;
- unsigned long final_buf_size;
-
- /* linked list of blames */
- struct blame_entry *ent;
-
- /* look-up a line in the final buffer */
- int num_lines;
- int *lineno;
-};
-
-static void sanity_check_refcnt(struct scoreboard *);
-
-/*
- * If two blame entries that are next to each other came from
- * contiguous lines in the same origin (i.e. <commit, path> pair),
- * merge them together.
- */
-static void coalesce(struct scoreboard *sb)
-{
- struct blame_entry *ent, *next;
-
- for (ent = sb->ent; ent && (next = ent->next); ent = next) {
- if (ent->suspect == next->suspect &&
- ent->s_lno + ent->num_lines == next->s_lno) {
- ent->num_lines += next->num_lines;
- ent->next = next->next;
- origin_decref(next->suspect);
- free(next);
- ent->score = 0;
- next = ent; /* again */
- }
- }
-
- if (DEBUG) /* sanity */
- sanity_check_refcnt(sb);
-}
-
-/*
- * Merge the given sorted list of blames into a preexisting origin.
- * If there were no previous blames to that commit, it is entered into
- * the commit priority queue of the score board.
- */
-
-static void queue_blames(struct scoreboard *sb, struct origin *porigin,
- struct blame_entry *sorted)
-{
- if (porigin->suspects)
- porigin->suspects = blame_merge(porigin->suspects, sorted);
- else {
- struct origin *o;
- for (o = porigin->commit->util; o; o = o->next) {
- if (o->suspects) {
- porigin->suspects = sorted;
- return;
- }
- }
- porigin->suspects = sorted;
- prio_queue_put(&sb->commits, porigin->commit);
- }
-}
-
-/*
- * Given a commit and a path in it, create a new origin structure.
- * The callers that add blame to the scoreboard should use
- * get_origin() to obtain shared, refcounted copy instead of calling
- * this function directly.
- */
-static struct origin *make_origin(struct commit *commit, const char *path)
-{
- struct origin *o;
- FLEX_ALLOC_STR(o, path, path);
- o->commit = commit;
- o->refcnt = 1;
- o->next = commit->util;
- commit->util = o;
- return o;
-}
-
-/*
- * Locate an existing origin or create a new one.
- * This moves the origin to front position in the commit util list.
- */
-static struct origin *get_origin(struct scoreboard *sb,
- struct commit *commit,
- const char *path)
-{
- struct origin *o, *l;
-
- for (o = commit->util, l = NULL; o; l = o, o = o->next) {
- if (!strcmp(o->path, path)) {
- /* bump to front */
- if (l) {
- l->next = o->next;
- o->next = commit->util;
- commit->util = o;
- }
- return origin_incref(o);
- }
- }
- return make_origin(commit, path);
-}
-
-/*
- * Fill the blob_sha1 field of an origin if it hasn't, so that later
- * call to fill_origin_blob() can use it to locate the data. blob_sha1
- * for an origin is also used to pass the blame for the entire file to
- * the parent to detect the case where a child's blob is identical to
- * that of its parent's.
- *
- * This also fills origin->mode for corresponding tree path.
- */
-static int fill_blob_sha1_and_mode(struct origin *origin)
-{
- if (!is_null_oid(&origin->blob_oid))
- return 0;
- if (get_tree_entry(origin->commit->object.oid.hash,
- origin->path,
- origin->blob_oid.hash, &origin->mode))
- goto error_out;
- if (sha1_object_info(origin->blob_oid.hash, NULL) != OBJ_BLOB)
- goto error_out;
- return 0;
- error_out:
- oidclr(&origin->blob_oid);
- origin->mode = S_IFINVALID;
- return -1;
-}
-
-/*
- * We have an origin -- check if the same path exists in the
- * parent and return an origin structure to represent it.
- */
-static struct origin *find_origin(struct scoreboard *sb,
- struct commit *parent,
- struct origin *origin)
-{
- struct origin *porigin;
- struct diff_options diff_opts;
- const char *paths[2];
-
- /* First check any existing origins */
- for (porigin = parent->util; porigin; porigin = porigin->next)
- if (!strcmp(porigin->path, origin->path)) {
- /*
- * The same path between origin and its parent
- * without renaming -- the most common case.
- */
- return origin_incref (porigin);
- }
-
- /* See if the origin->path is different between parent
- * and origin first. Most of the time they are the
- * same and diff-tree is fairly efficient about this.
- */
- diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- diff_opts.detect_rename = 0;
- diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
- paths[0] = origin->path;
- paths[1] = NULL;
-
- parse_pathspec(&diff_opts.pathspec,
- PATHSPEC_ALL_MAGIC & ~PATHSPEC_LITERAL,
- PATHSPEC_LITERAL_PATH, "", paths);
- diff_setup_done(&diff_opts);
-
- if (is_null_oid(&origin->commit->object.oid))
- do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
- else
- diff_tree_sha1(parent->tree->object.oid.hash,
- origin->commit->tree->object.oid.hash,
- "", &diff_opts);
- diffcore_std(&diff_opts);
-
- if (!diff_queued_diff.nr) {
- /* The path is the same as parent */
- porigin = get_origin(sb, parent, origin->path);
- oidcpy(&porigin->blob_oid, &origin->blob_oid);
- porigin->mode = origin->mode;
- } else {
- /*
- * Since origin->path is a pathspec, if the parent
- * commit had it as a directory, we will see a whole
- * bunch of deletion of files in the directory that we
- * do not care about.
- */
- int i;
- struct diff_filepair *p = NULL;
- for (i = 0; i < diff_queued_diff.nr; i++) {
- const char *name;
- p = diff_queued_diff.queue[i];
- name = p->one->path ? p->one->path : p->two->path;
- if (!strcmp(name, origin->path))
- break;
- }
- if (!p)
- die("internal error in blame::find_origin");
- switch (p->status) {
- default:
- die("internal error in blame::find_origin (%c)",
- p->status);
- case 'M':
- porigin = get_origin(sb, parent, origin->path);
- oidcpy(&porigin->blob_oid, &p->one->oid);
- porigin->mode = p->one->mode;
- break;
- case 'A':
- case 'T':
- /* Did not exist in parent, or type changed */
- break;
- }
- }
- diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
- return porigin;
-}
-
-/*
- * We have an origin -- find the path that corresponds to it in its
- * parent and return an origin structure to represent it.
- */
-static struct origin *find_rename(struct scoreboard *sb,
- struct commit *parent,
- struct origin *origin)
-{
- struct origin *porigin = NULL;
- struct diff_options diff_opts;
- int i;
-
- diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- diff_opts.detect_rename = DIFF_DETECT_RENAME;
- diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
- diff_opts.single_follow = origin->path;
- diff_setup_done(&diff_opts);
-
- if (is_null_oid(&origin->commit->object.oid))
- do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
- else
- diff_tree_sha1(parent->tree->object.oid.hash,
- origin->commit->tree->object.oid.hash,
- "", &diff_opts);
- diffcore_std(&diff_opts);
-
- for (i = 0; i < diff_queued_diff.nr; i++) {
- struct diff_filepair *p = diff_queued_diff.queue[i];
- if ((p->status == 'R' || p->status == 'C') &&
- !strcmp(p->two->path, origin->path)) {
- porigin = get_origin(sb, parent, p->one->path);
- oidcpy(&porigin->blob_oid, &p->one->oid);
- porigin->mode = p->one->mode;
- break;
- }
- }
- diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
- return porigin;
-}
-
-/*
- * Append a new blame entry to a given output queue.
- */
-static void add_blame_entry(struct blame_entry ***queue,
- const struct blame_entry *src)
-{
- struct blame_entry *e = xmalloc(sizeof(*e));
- memcpy(e, src, sizeof(*e));
- origin_incref(e->suspect);
-
- e->next = **queue;
- **queue = e;
- *queue = &e->next;
-}
-
-/*
- * src typically is on-stack; we want to copy the information in it to
- * a malloced blame_entry that gets added to the given queue. The
- * origin of dst loses a refcnt.
- */
-static void dup_entry(struct blame_entry ***queue,
- struct blame_entry *dst, struct blame_entry *src)
-{
- origin_incref(src->suspect);
- origin_decref(dst->suspect);
- memcpy(dst, src, sizeof(*src));
- dst->next = **queue;
- **queue = dst;
- *queue = &dst->next;
-}
-
-static const char *nth_line(struct scoreboard *sb, long lno)
-{
- return sb->final_buf + sb->lineno[lno];
-}
-
static const char *nth_line_cb(void *data, long lno)
{
- return nth_line((struct scoreboard *)data, lno);
-}
-
-/*
- * It is known that lines between tlno to same came from parent, and e
- * has an overlap with that range. it also is known that parent's
- * line plno corresponds to e's line tlno.
- *
- * <---- e ----->
- * <------>
- * <------------>
- * <------------>
- * <------------------>
- *
- * Split e into potentially three parts; before this chunk, the chunk
- * to be blamed for the parent, and after that portion.
- */
-static void split_overlap(struct blame_entry *split,
- struct blame_entry *e,
- int tlno, int plno, int same,
- struct origin *parent)
-{
- int chunk_end_lno;
- memset(split, 0, sizeof(struct blame_entry [3]));
-
- if (e->s_lno < tlno) {
- /* there is a pre-chunk part not blamed on parent */
- split[0].suspect = origin_incref(e->suspect);
- split[0].lno = e->lno;
- split[0].s_lno = e->s_lno;
- split[0].num_lines = tlno - e->s_lno;
- split[1].lno = e->lno + tlno - e->s_lno;
- split[1].s_lno = plno;
- }
- else {
- split[1].lno = e->lno;
- split[1].s_lno = plno + (e->s_lno - tlno);
- }
-
- if (same < e->s_lno + e->num_lines) {
- /* there is a post-chunk part not blamed on parent */
- split[2].suspect = origin_incref(e->suspect);
- split[2].lno = e->lno + (same - e->s_lno);
- split[2].s_lno = e->s_lno + (same - e->s_lno);
- split[2].num_lines = e->s_lno + e->num_lines - same;
- chunk_end_lno = split[2].lno;
- }
- else
- chunk_end_lno = e->lno + e->num_lines;
- split[1].num_lines = chunk_end_lno - split[1].lno;
-
- /*
- * if it turns out there is nothing to blame the parent for,
- * forget about the splitting. !split[1].suspect signals this.
- */
- if (split[1].num_lines < 1)
- return;
- split[1].suspect = origin_incref(parent);
-}
-
-/*
- * split_overlap() divided an existing blame e into up to three parts
- * in split. Any assigned blame is moved to queue to
- * reflect the split.
- */
-static void split_blame(struct blame_entry ***blamed,
- struct blame_entry ***unblamed,
- struct blame_entry *split,
- struct blame_entry *e)
-{
- if (split[0].suspect && split[2].suspect) {
- /* The first part (reuse storage for the existing entry e) */
- dup_entry(unblamed, e, &split[0]);
-
- /* The last part -- me */
- add_blame_entry(unblamed, &split[2]);
-
- /* ... and the middle part -- parent */
- add_blame_entry(blamed, &split[1]);
- }
- else if (!split[0].suspect && !split[2].suspect)
- /*
- * The parent covers the entire area; reuse storage for
- * e and replace it with the parent.
- */
- dup_entry(blamed, e, &split[1]);
- else if (split[0].suspect) {
- /* me and then parent */
- dup_entry(unblamed, e, &split[0]);
- add_blame_entry(blamed, &split[1]);
- }
- else {
- /* parent and then me */
- dup_entry(blamed, e, &split[1]);
- add_blame_entry(unblamed, &split[2]);
- }
-}
-
-/*
- * After splitting the blame, the origins used by the
- * on-stack blame_entry should lose one refcnt each.
- */
-static void decref_split(struct blame_entry *split)
-{
- int i;
-
- for (i = 0; i < 3; i++)
- origin_decref(split[i].suspect);
-}
-
-/*
- * reverse_blame reverses the list given in head, appending tail.
- * That allows us to build lists in reverse order, then reverse them
- * afterwards. This can be faster than building the list in proper
- * order right away. The reason is that building in proper order
- * requires writing a link in the _previous_ element, while building
- * in reverse order just requires placing the list head into the
- * _current_ element.
- */
-
-static struct blame_entry *reverse_blame(struct blame_entry *head,
- struct blame_entry *tail)
-{
- while (head) {
- struct blame_entry *next = head->next;
- head->next = tail;
- tail = head;
- head = next;
- }
- return tail;
-}
-
-/*
- * Process one hunk from the patch between the current suspect for
- * blame_entry e and its parent. This first blames any unfinished
- * entries before the chunk (which is where target and parent start
- * differing) on the parent, and then splits blame entries at the
- * start and at the end of the difference region. Since use of -M and
- * -C options may lead to overlapping/duplicate source line number
- * ranges, all we can rely on from sorting/merging is the order of the
- * first suspect line number.
- */
-static void blame_chunk(struct blame_entry ***dstq, struct blame_entry ***srcq,
- int tlno, int offset, int same,
- struct origin *parent)
-{
- struct blame_entry *e = **srcq;
- struct blame_entry *samep = NULL, *diffp = NULL;
-
- while (e && e->s_lno < tlno) {
- struct blame_entry *next = e->next;
- /*
- * current record starts before differing portion. If
- * it reaches into it, we need to split it up and
- * examine the second part separately.
- */
- if (e->s_lno + e->num_lines > tlno) {
- /* Move second half to a new record */
- int len = tlno - e->s_lno;
- struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
- n->suspect = e->suspect;
- n->lno = e->lno + len;
- n->s_lno = e->s_lno + len;
- n->num_lines = e->num_lines - len;
- e->num_lines = len;
- e->score = 0;
- /* Push new record to diffp */
- n->next = diffp;
- diffp = n;
- } else
- origin_decref(e->suspect);
- /* Pass blame for everything before the differing
- * chunk to the parent */
- e->suspect = origin_incref(parent);
- e->s_lno += offset;
- e->next = samep;
- samep = e;
- e = next;
- }
- /*
- * As we don't know how much of a common stretch after this
- * diff will occur, the currently blamed parts are all that we
- * can assign to the parent for now.
- */
-
- if (samep) {
- **dstq = reverse_blame(samep, **dstq);
- *dstq = &samep->next;
- }
- /*
- * Prepend the split off portions: everything after e starts
- * after the blameable portion.
- */
- e = reverse_blame(diffp, e);
-
- /*
- * Now retain records on the target while parts are different
- * from the parent.
- */
- samep = NULL;
- diffp = NULL;
- while (e && e->s_lno < same) {
- struct blame_entry *next = e->next;
-
- /*
- * If current record extends into sameness, need to split.
- */
- if (e->s_lno + e->num_lines > same) {
- /*
- * Move second half to a new record to be
- * processed by later chunks
- */
- int len = same - e->s_lno;
- struct blame_entry *n = xcalloc(1, sizeof (struct blame_entry));
- n->suspect = origin_incref(e->suspect);
- n->lno = e->lno + len;
- n->s_lno = e->s_lno + len;
- n->num_lines = e->num_lines - len;
- e->num_lines = len;
- e->score = 0;
- /* Push new record to samep */
- n->next = samep;
- samep = n;
- }
- e->next = diffp;
- diffp = e;
- e = next;
- }
- **srcq = reverse_blame(diffp, reverse_blame(samep, e));
- /* Move across elements that are in the unblamable portion */
- if (diffp)
- *srcq = &diffp->next;
-}
-
-struct blame_chunk_cb_data {
- struct origin *parent;
- long offset;
- struct blame_entry **dstq;
- struct blame_entry **srcq;
-};
-
-/* diff chunks are from parent to target */
-static int blame_chunk_cb(long start_a, long count_a,
- long start_b, long count_b, void *data)
-{
- struct blame_chunk_cb_data *d = data;
- if (start_a - start_b != d->offset)
- die("internal error in blame::blame_chunk_cb");
- blame_chunk(&d->dstq, &d->srcq, start_b, start_a - start_b,
- start_b + count_b, d->parent);
- d->offset = start_a + count_a - (start_b + count_b);
- return 0;
-}
-
-/*
- * We are looking at the origin 'target' and aiming to pass blame
- * for the lines it is suspected to its parent. Run diff to find
- * which lines came from parent and pass blame for them.
- */
-static void pass_blame_to_parent(struct scoreboard *sb,
- struct origin *target,
- struct origin *parent)
-{
- mmfile_t file_p, file_o;
- struct blame_chunk_cb_data d;
- struct blame_entry *newdest = NULL;
-
- if (!target->suspects)
- return; /* nothing remains for this target */
-
- d.parent = parent;
- d.offset = 0;
- d.dstq = &newdest; d.srcq = &target->suspects;
-
- fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
- fill_origin_blob(&sb->revs->diffopt, target, &file_o);
- num_get_patch++;
-
- if (diff_hunks(&file_p, &file_o, blame_chunk_cb, &d))
- die("unable to generate diff (%s -> %s)",
- oid_to_hex(&parent->commit->object.oid),
- oid_to_hex(&target->commit->object.oid));
- /* The rest are the same as the parent */
- blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
- *d.dstq = NULL;
- queue_blames(sb, parent, newdest);
-
- return;
-}
-
-/*
- * The lines in blame_entry after splitting blames many times can become
- * very small and trivial, and at some point it becomes pointless to
- * blame the parents. E.g. "\t\t}\n\t}\n\n" appears everywhere in any
- * ordinary C program, and it is not worth to say it was copied from
- * totally unrelated file in the parent.
- *
- * Compute how trivial the lines in the blame_entry are.
- */
-static unsigned ent_score(struct scoreboard *sb, struct blame_entry *e)
-{
- unsigned score;
- const char *cp, *ep;
-
- if (e->score)
- return e->score;
-
- score = 1;
- cp = nth_line(sb, e->lno);
- ep = nth_line(sb, e->lno + e->num_lines);
- while (cp < ep) {
- unsigned ch = *((unsigned char *)cp);
- if (isalnum(ch))
- score++;
- cp++;
- }
- e->score = score;
- return score;
-}
-
-/*
- * best_so_far[] and this[] are both a split of an existing blame_entry
- * that passes blame to the parent. Maintain best_so_far the best split
- * so far, by comparing this and best_so_far and copying this into
- * bst_so_far as needed.
- */
-static void copy_split_if_better(struct scoreboard *sb,
- struct blame_entry *best_so_far,
- struct blame_entry *this)
-{
- int i;
-
- if (!this[1].suspect)
- return;
- if (best_so_far[1].suspect) {
- if (ent_score(sb, &this[1]) < ent_score(sb, &best_so_far[1]))
- return;
- }
-
- for (i = 0; i < 3; i++)
- origin_incref(this[i].suspect);
- decref_split(best_so_far);
- memcpy(best_so_far, this, sizeof(struct blame_entry [3]));
-}
-
-/*
- * We are looking at a part of the final image represented by
- * ent (tlno and same are offset by ent->s_lno).
- * tlno is where we are looking at in the final image.
- * up to (but not including) same match preimage.
- * plno is where we are looking at in the preimage.
- *
- * <-------------- final image ---------------------->
- * <------ent------>
- * ^tlno ^same
- * <---------preimage----->
- * ^plno
- *
- * All line numbers are 0-based.
- */
-static void handle_split(struct scoreboard *sb,
- struct blame_entry *ent,
- int tlno, int plno, int same,
- struct origin *parent,
- struct blame_entry *split)
-{
- if (ent->num_lines <= tlno)
- return;
- if (tlno < same) {
- struct blame_entry this[3];
- tlno += ent->s_lno;
- same += ent->s_lno;
- split_overlap(this, ent, tlno, plno, same, parent);
- copy_split_if_better(sb, split, this);
- decref_split(this);
- }
-}
-
-struct handle_split_cb_data {
- struct scoreboard *sb;
- struct blame_entry *ent;
- struct origin *parent;
- struct blame_entry *split;
- long plno;
- long tlno;
-};
-
-static int handle_split_cb(long start_a, long count_a,
- long start_b, long count_b, void *data)
-{
- struct handle_split_cb_data *d = data;
- handle_split(d->sb, d->ent, d->tlno, d->plno, start_b, d->parent,
- d->split);
- d->plno = start_a + count_a;
- d->tlno = start_b + count_b;
- return 0;
-}
-
-/*
- * Find the lines from parent that are the same as ent so that
- * we can pass blames to it. file_p has the blob contents for
- * the parent.
- */
-static void find_copy_in_blob(struct scoreboard *sb,
- struct blame_entry *ent,
- struct origin *parent,
- struct blame_entry *split,
- mmfile_t *file_p)
-{
- const char *cp;
- mmfile_t file_o;
- struct handle_split_cb_data d;
-
- memset(&d, 0, sizeof(d));
- d.sb = sb; d.ent = ent; d.parent = parent; d.split = split;
- /*
- * Prepare mmfile that contains only the lines in ent.
- */
- cp = nth_line(sb, ent->lno);
- file_o.ptr = (char *) cp;
- file_o.size = nth_line(sb, ent->lno + ent->num_lines) - cp;
-
- /*
- * file_o is a part of final image we are annotating.
- * file_p partially may match that image.
- */
- memset(split, 0, sizeof(struct blame_entry [3]));
- if (diff_hunks(file_p, &file_o, handle_split_cb, &d))
- die("unable to generate diff (%s)",
- oid_to_hex(&parent->commit->object.oid));
- /* remainder, if any, all match the preimage */
- handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
-}
-
-/* Move all blame entries from list *source that have a score smaller
- * than score_min to the front of list *small.
- * Returns a pointer to the link pointing to the old head of the small list.
- */
-
-static struct blame_entry **filter_small(struct scoreboard *sb,
- struct blame_entry **small,
- struct blame_entry **source,
- unsigned score_min)
-{
- struct blame_entry *p = *source;
- struct blame_entry *oldsmall = *small;
- while (p) {
- if (ent_score(sb, p) <= score_min) {
- *small = p;
- small = &p->next;
- p = *small;
- } else {
- *source = p;
- source = &p->next;
- p = *source;
- }
- }
- *small = oldsmall;
- *source = NULL;
- return small;
-}
-
-/*
- * See if lines currently target is suspected for can be attributed to
- * parent.
- */
-static void find_move_in_parent(struct scoreboard *sb,
- struct blame_entry ***blamed,
- struct blame_entry **toosmall,
- struct origin *target,
- struct origin *parent)
-{
- struct blame_entry *e, split[3];
- struct blame_entry *unblamed = target->suspects;
- struct blame_entry *leftover = NULL;
- mmfile_t file_p;
-
- if (!unblamed)
- return; /* nothing remains for this target */
-
- fill_origin_blob(&sb->revs->diffopt, parent, &file_p);
- if (!file_p.ptr)
- return;
-
- /* At each iteration, unblamed has a NULL-terminated list of
- * entries that have not yet been tested for blame. leftover
- * contains the reversed list of entries that have been tested
- * without being assignable to the parent.
- */
- do {
- struct blame_entry **unblamedtail = &unblamed;
- struct blame_entry *next;
- for (e = unblamed; e; e = next) {
- next = e->next;
- find_copy_in_blob(sb, e, parent, split, &file_p);
- if (split[1].suspect &&
- blame_move_score < ent_score(sb, &split[1])) {
- split_blame(blamed, &unblamedtail, split, e);
- } else {
- e->next = leftover;
- leftover = e;
- }
- decref_split(split);
- }
- *unblamedtail = NULL;
- toosmall = filter_small(sb, toosmall, &unblamed, blame_move_score);
- } while (unblamed);
- target->suspects = reverse_blame(leftover, NULL);
-}
-
-struct blame_list {
- struct blame_entry *ent;
- struct blame_entry split[3];
-};
-
-/*
- * Count the number of entries the target is suspected for,
- * and prepare a list of entry and the best split.
- */
-static struct blame_list *setup_blame_list(struct blame_entry *unblamed,
- int *num_ents_p)
-{
- struct blame_entry *e;
- int num_ents, i;
- struct blame_list *blame_list = NULL;
-
- for (e = unblamed, num_ents = 0; e; e = e->next)
- num_ents++;
- if (num_ents) {
- blame_list = xcalloc(num_ents, sizeof(struct blame_list));
- for (e = unblamed, i = 0; e; e = e->next)
- blame_list[i++].ent = e;
- }
- *num_ents_p = num_ents;
- return blame_list;
-}
-
-/*
- * For lines target is suspected for, see if we can find code movement
- * across file boundary from the parent commit. porigin is the path
- * in the parent we already tried.
- */
-static void find_copy_in_parent(struct scoreboard *sb,
- struct blame_entry ***blamed,
- struct blame_entry **toosmall,
- struct origin *target,
- struct commit *parent,
- struct origin *porigin,
- int opt)
-{
- struct diff_options diff_opts;
- int i, j;
- struct blame_list *blame_list;
- int num_ents;
- struct blame_entry *unblamed = target->suspects;
- struct blame_entry *leftover = NULL;
-
- if (!unblamed)
- return; /* nothing remains for this target */
-
- diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT;
-
- diff_setup_done(&diff_opts);
-
- /* Try "find copies harder" on new path if requested;
- * we do not want to use diffcore_rename() actually to
- * match things up; find_copies_harder is set only to
- * force diff_tree_sha1() to feed all filepairs to diff_queue,
- * and this code needs to be after diff_setup_done(), which
- * usually makes find-copies-harder imply copy detection.
- */
- if ((opt & PICKAXE_BLAME_COPY_HARDEST)
- || ((opt & PICKAXE_BLAME_COPY_HARDER)
- && (!porigin || strcmp(target->path, porigin->path))))
- DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
-
- if (is_null_oid(&target->commit->object.oid))
- do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
- else
- diff_tree_sha1(parent->tree->object.oid.hash,
- target->commit->tree->object.oid.hash,
- "", &diff_opts);
-
- if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
- diffcore_std(&diff_opts);
-
- do {
- struct blame_entry **unblamedtail = &unblamed;
- blame_list = setup_blame_list(unblamed, &num_ents);
-
- for (i = 0; i < diff_queued_diff.nr; i++) {
- struct diff_filepair *p = diff_queued_diff.queue[i];
- struct origin *norigin;
- mmfile_t file_p;
- struct blame_entry this[3];
-
- if (!DIFF_FILE_VALID(p->one))
- continue; /* does not exist in parent */
- if (S_ISGITLINK(p->one->mode))
- continue; /* ignore git links */
- if (porigin && !strcmp(p->one->path, porigin->path))
- /* find_move already dealt with this path */
- continue;
-
- norigin = get_origin(sb, parent, p->one->path);
- oidcpy(&norigin->blob_oid, &p->one->oid);
- norigin->mode = p->one->mode;
- fill_origin_blob(&sb->revs->diffopt, norigin, &file_p);
- if (!file_p.ptr)
- continue;
-
- for (j = 0; j < num_ents; j++) {
- find_copy_in_blob(sb, blame_list[j].ent,
- norigin, this, &file_p);
- copy_split_if_better(sb, blame_list[j].split,
- this);
- decref_split(this);
- }
- origin_decref(norigin);
- }
-
- for (j = 0; j < num_ents; j++) {
- struct blame_entry *split = blame_list[j].split;
- if (split[1].suspect &&
- blame_copy_score < ent_score(sb, &split[1])) {
- split_blame(blamed, &unblamedtail, split,
- blame_list[j].ent);
- } else {
- blame_list[j].ent->next = leftover;
- leftover = blame_list[j].ent;
- }
- decref_split(split);
- }
- free(blame_list);
- *unblamedtail = NULL;
- toosmall = filter_small(sb, toosmall, &unblamed, blame_copy_score);
- } while (unblamed);
- target->suspects = reverse_blame(leftover, NULL);
- diff_flush(&diff_opts);
- clear_pathspec(&diff_opts.pathspec);
-}
-
-/*
- * The blobs of origin and porigin exactly match, so everything
- * origin is suspected for can be blamed on the parent.
- */
-static void pass_whole_blame(struct scoreboard *sb,
- struct origin *origin, struct origin *porigin)
-{
- struct blame_entry *e, *suspects;
-
- if (!porigin->file.ptr && origin->file.ptr) {
- /* Steal its file */
- porigin->file = origin->file;
- origin->file.ptr = NULL;
- }
- suspects = origin->suspects;
- origin->suspects = NULL;
- for (e = suspects; e; e = e->next) {
- origin_incref(porigin);
- origin_decref(e->suspect);
- e->suspect = porigin;
- }
- queue_blames(sb, porigin, suspects);
-}
-
-/*
- * We pass blame from the current commit to its parents. We keep saying
- * "parent" (and "porigin"), but what we mean is to find scapegoat to
- * exonerate ourselves.
- */
-static struct commit_list *first_scapegoat(struct rev_info *revs, struct commit *commit)
-{
- if (!reverse) {
- if (revs->first_parent_only &&
- commit->parents &&
- commit->parents->next) {
- free_commit_list(commit->parents->next);
- commit->parents->next = NULL;
- }
- return commit->parents;
- }
- return lookup_decoration(&revs->children, &commit->object);
-}
-
-static int num_scapegoats(struct rev_info *revs, struct commit *commit)
-{
- struct commit_list *l = first_scapegoat(revs, commit);
- return commit_list_count(l);
-}
-
-/* Distribute collected unsorted blames to the respected sorted lists
- * in the various origins.
- */
-static void distribute_blame(struct scoreboard *sb, struct blame_entry *blamed)
-{
- blamed = blame_sort(blamed, compare_blame_suspect);
- while (blamed)
- {
- struct origin *porigin = blamed->suspect;
- struct blame_entry *suspects = NULL;
- do {
- struct blame_entry *next = blamed->next;
- blamed->next = suspects;
- suspects = blamed;
- blamed = next;
- } while (blamed && blamed->suspect == porigin);
- suspects = reverse_blame(suspects, NULL);
- queue_blames(sb, porigin, suspects);
- }
-}
-
-#define MAXSG 16
-
-static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
-{
- struct rev_info *revs = sb->revs;
- int i, pass, num_sg;
- struct commit *commit = origin->commit;
- struct commit_list *sg;
- struct origin *sg_buf[MAXSG];
- struct origin *porigin, **sg_origin = sg_buf;
- struct blame_entry *toosmall = NULL;
- struct blame_entry *blames, **blametail = &blames;
-
- num_sg = num_scapegoats(revs, commit);
- if (!num_sg)
- goto finish;
- else if (num_sg < ARRAY_SIZE(sg_buf))
- memset(sg_buf, 0, sizeof(sg_buf));
- else
- sg_origin = xcalloc(num_sg, sizeof(*sg_origin));
-
- /*
- * The first pass looks for unrenamed path to optimize for
- * common cases, then we look for renames in the second pass.
- */
- for (pass = 0; pass < 2 - no_whole_file_rename; pass++) {
- struct origin *(*find)(struct scoreboard *,
- struct commit *, struct origin *);
- find = pass ? find_rename : find_origin;
-
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct commit *p = sg->item;
- int j, same;
-
- if (sg_origin[i])
- continue;
- if (parse_commit(p))
- continue;
- porigin = find(sb, p, origin);
- if (!porigin)
- continue;
- if (!oidcmp(&porigin->blob_oid, &origin->blob_oid)) {
- pass_whole_blame(sb, origin, porigin);
- origin_decref(porigin);
- goto finish;
- }
- for (j = same = 0; j < i; j++)
- if (sg_origin[j] &&
- !oidcmp(&sg_origin[j]->blob_oid, &porigin->blob_oid)) {
- same = 1;
- break;
- }
- if (!same)
- sg_origin[i] = porigin;
- else
- origin_decref(porigin);
- }
- }
-
- num_commits++;
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct origin *porigin = sg_origin[i];
- if (!porigin)
- continue;
- if (!origin->previous) {
- origin_incref(porigin);
- origin->previous = porigin;
- }
- pass_blame_to_parent(sb, origin, porigin);
- if (!origin->suspects)
- goto finish;
- }
-
- /*
- * Optionally find moves in parents' files.
- */
- if (opt & PICKAXE_BLAME_MOVE) {
- filter_small(sb, &toosmall, &origin->suspects, blame_move_score);
- if (origin->suspects) {
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct origin *porigin = sg_origin[i];
- if (!porigin)
- continue;
- find_move_in_parent(sb, &blametail, &toosmall, origin, porigin);
- if (!origin->suspects)
- break;
- }
- }
- }
-
- /*
- * Optionally find copies from parents' files.
- */
- if (opt & PICKAXE_BLAME_COPY) {
- if (blame_copy_score > blame_move_score)
- filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
- else if (blame_copy_score < blame_move_score) {
- origin->suspects = blame_merge(origin->suspects, toosmall);
- toosmall = NULL;
- filter_small(sb, &toosmall, &origin->suspects, blame_copy_score);
- }
- if (!origin->suspects)
- goto finish;
-
- for (i = 0, sg = first_scapegoat(revs, commit);
- i < num_sg && sg;
- sg = sg->next, i++) {
- struct origin *porigin = sg_origin[i];
- find_copy_in_parent(sb, &blametail, &toosmall,
- origin, sg->item, porigin, opt);
- if (!origin->suspects)
- goto finish;
- }
- }
-
-finish:
- *blametail = NULL;
- distribute_blame(sb, blames);
- /*
- * prepend toosmall to origin->suspects
- *
- * There is no point in sorting: this ends up on a big
- * unsorted list in the caller anyway.
- */
- if (toosmall) {
- struct blame_entry **tail = &toosmall;
- while (*tail)
- tail = &(*tail)->next;
- *tail = origin->suspects;
- origin->suspects = toosmall;
- }
- for (i = 0; i < num_sg; i++) {
- if (sg_origin[i]) {
- drop_origin_blob(sg_origin[i]);
- origin_decref(sg_origin[i]);
- }
- }
- drop_origin_blob(origin);
- if (sg_buf != sg_origin)
- free(sg_origin);
+ return blame_nth_line((struct blame_scoreboard *)data, lno);
}
/*
@@ -1561,13 +78,13 @@ finish:
struct commit_info {
struct strbuf author;
struct strbuf author_mail;
- unsigned long author_time;
+ timestamp_t author_time;
struct strbuf author_tz;
/* filled only when asked for details */
struct strbuf committer;
struct strbuf committer_mail;
- unsigned long committer_time;
+ timestamp_t committer_time;
struct strbuf committer_tz;
struct strbuf summary;
@@ -1578,7 +95,7 @@ struct commit_info {
*/
static void get_ac_line(const char *inbuf, const char *what,
struct strbuf *name, struct strbuf *mail,
- unsigned long *time, struct strbuf *tz)
+ timestamp_t *time, struct strbuf *tz)
{
struct ident_split ident;
size_t len, maillen, namelen;
@@ -1699,10 +216,10 @@ static void get_commit_info(struct commit *commit,
* To allow LF and other nonportable characters in pathnames,
* they are c-style quoted as needed.
*/
-static void write_filename_info(struct origin *suspect)
+static void write_filename_info(struct blame_origin *suspect)
{
if (suspect->previous) {
- struct origin *prev = suspect->previous;
+ struct blame_origin *prev = suspect->previous;
printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
write_name_quoted(prev->path, stdout, '\n');
}
@@ -1716,7 +233,7 @@ static void write_filename_info(struct origin *suspect)
* the first time each commit appears in the output (unless the
* user has specifically asked for us to repeat).
*/
-static int emit_one_suspect_detail(struct origin *suspect, int repeat)
+static int emit_one_suspect_detail(struct blame_origin *suspect, int repeat)
{
struct commit_info ci;
@@ -1727,11 +244,11 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
get_commit_info(suspect->commit, &ci, 1);
printf("author %s\n", ci.author.buf);
printf("author-mail %s\n", ci.author_mail.buf);
- printf("author-time %lu\n", ci.author_time);
+ printf("author-time %"PRItime"\n", ci.author_time);
printf("author-tz %s\n", ci.author_tz.buf);
printf("committer %s\n", ci.committer.buf);
printf("committer-mail %s\n", ci.committer_mail.buf);
- printf("committer-time %lu\n", ci.committer_time);
+ printf("committer-time %"PRItime"\n", ci.committer_time);
printf("committer-tz %s\n", ci.committer_tz.buf);
printf("summary %s\n", ci.summary.buf);
if (suspect->commit->object.flags & UNINTERESTING)
@@ -1746,11 +263,12 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
* The blame_entry is found to be guilty for the range.
* Show it in incremental output.
*/
-static void found_guilty_entry(struct blame_entry *ent,
- struct progress_info *pi)
+static void found_guilty_entry(struct blame_entry *ent, void *data)
{
+ struct progress_info *pi = (struct progress_info *)data;
+
if (incremental) {
- struct origin *suspect = ent->suspect;
+ struct blame_origin *suspect = ent->suspect;
printf("%s %d %d %d\n",
oid_to_hex(&suspect->commit->object.oid),
@@ -1763,88 +281,14 @@ static void found_guilty_entry(struct blame_entry *ent,
display_progress(pi->progress, pi->blamed_lines);
}
-/*
- * The main loop -- while we have blobs with lines whose true origin
- * is still unknown, pick one blob, and allow its lines to pass blames
- * to its parents. */
-static void assign_blame(struct scoreboard *sb, int opt)
-{
- struct rev_info *revs = sb->revs;
- struct commit *commit = prio_queue_get(&sb->commits);
- struct progress_info pi = { NULL, 0 };
-
- if (show_progress)
- pi.progress = start_progress_delay(_("Blaming lines"),
- sb->num_lines, 50, 1);
-
- while (commit) {
- struct blame_entry *ent;
- struct origin *suspect = commit->util;
-
- /* find one suspect to break down */
- while (suspect && !suspect->suspects)
- suspect = suspect->next;
-
- if (!suspect) {
- commit = prio_queue_get(&sb->commits);
- continue;
- }
-
- assert(commit == suspect->commit);
-
- /*
- * We will use this suspect later in the loop,
- * so hold onto it in the meantime.
- */
- origin_incref(suspect);
- parse_commit(commit);
- if (reverse ||
- (!(commit->object.flags & UNINTERESTING) &&
- !(revs->max_age != -1 && commit->date < revs->max_age)))
- pass_blame(sb, suspect, opt);
- else {
- commit->object.flags |= UNINTERESTING;
- if (commit->object.parsed)
- mark_parents_uninteresting(commit);
- }
- /* treat root commit as boundary */
- if (!commit->parents && !show_root)
- commit->object.flags |= UNINTERESTING;
-
- /* Take responsibility for the remaining entries */
- ent = suspect->suspects;
- if (ent) {
- suspect->guilty = 1;
- for (;;) {
- struct blame_entry *next = ent->next;
- found_guilty_entry(ent, &pi);
- if (next) {
- ent = next;
- continue;
- }
- ent->next = sb->ent;
- sb->ent = suspect->suspects;
- suspect->suspects = NULL;
- break;
- }
- }
- origin_decref(suspect);
-
- if (DEBUG) /* sanity */
- sanity_check_refcnt(sb);
- }
-
- stop_progress(&pi.progress);
-}
-
-static const char *format_time(unsigned long time, const char *tz_str,
+static const char *format_time(timestamp_t time, const char *tz_str,
int show_raw_time)
{
static struct strbuf time_buf = STRBUF_INIT;
strbuf_reset(&time_buf);
if (show_raw_time) {
- strbuf_addf(&time_buf, "%lu %s", time, tz_str);
+ strbuf_addf(&time_buf, "%"PRItime" %s", time, tz_str);
}
else {
const char *time_str;
@@ -1876,20 +320,20 @@ static const char *format_time(unsigned long time, const char *tz_str,
#define OUTPUT_SHOW_EMAIL 0400
#define OUTPUT_LINE_PORCELAIN 01000
-static void emit_porcelain_details(struct origin *suspect, int repeat)
+static void emit_porcelain_details(struct blame_origin *suspect, int repeat)
{
if (emit_one_suspect_detail(suspect, repeat) ||
(suspect->commit->object.flags & MORE_THAN_ONE_PATH))
write_filename_info(suspect);
}
-static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
+static void emit_porcelain(struct blame_scoreboard *sb, struct blame_entry *ent,
int opt)
{
int repeat = opt & OUTPUT_LINE_PORCELAIN;
int cnt;
const char *cp;
- struct origin *suspect = ent->suspect;
+ struct blame_origin *suspect = ent->suspect;
char hex[GIT_MAX_HEXSZ + 1];
oid_to_hex_r(hex, &suspect->commit->object.oid);
@@ -1900,7 +344,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
ent->num_lines);
emit_porcelain_details(suspect, repeat);
- cp = nth_line(sb, ent->lno);
+ cp = blame_nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
if (cnt) {
@@ -1922,11 +366,11 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
putchar('\n');
}
-static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
+static void emit_other(struct blame_scoreboard *sb, struct blame_entry *ent, int opt)
{
int cnt;
const char *cp;
- struct origin *suspect = ent->suspect;
+ struct blame_origin *suspect = ent->suspect;
struct commit_info ci;
char hex[GIT_MAX_HEXSZ + 1];
int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
@@ -1934,7 +378,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
get_commit_info(suspect->commit, &ci, 1);
oid_to_hex_r(hex, &suspect->commit->object.oid);
- cp = nth_line(sb, ent->lno);
+ cp = blame_nth_line(sb, ent->lno);
for (cnt = 0; cnt < ent->num_lines; cnt++) {
char ch;
int length = (opt & OUTPUT_LONG_OBJECT_NAME) ? GIT_SHA1_HEXSZ : abbrev;
@@ -2001,14 +445,14 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
commit_info_destroy(&ci);
}
-static void output(struct scoreboard *sb, int option)
+static void output(struct blame_scoreboard *sb, int option)
{
struct blame_entry *ent;
if (option & OUTPUT_PORCELAIN) {
for (ent = sb->ent; ent; ent = ent->next) {
int count = 0;
- struct origin *suspect;
+ struct blame_origin *suspect;
struct commit *commit = ent->suspect->commit;
if (commit->object.flags & MORE_THAN_ONE_PATH)
continue;
@@ -2030,40 +474,6 @@ static void output(struct scoreboard *sb, int option)
}
}
-static const char *get_next_line(const char *start, const char *end)
-{
- const char *nl = memchr(start, '\n', end - start);
- return nl ? nl + 1 : end;
-}
-
-/*
- * To allow quick access to the contents of nth line in the
- * final image, prepare an index in the scoreboard.
- */
-static int prepare_lines(struct scoreboard *sb)
-{
- const char *buf = sb->final_buf;
- unsigned long len = sb->final_buf_size;
- const char *end = buf + len;
- const char *p;
- int *lineno;
- int num = 0;
-
- for (p = buf; p < end; p = get_next_line(p, end))
- num++;
-
- ALLOC_ARRAY(sb->lineno, num + 1);
- lineno = sb->lineno;
-
- for (p = buf; p < end; p = get_next_line(p, end))
- *lineno++ = p - buf;
-
- *lineno = len;
-
- sb->num_lines = num;
- return sb->num_lines;
-}
-
/*
* Add phony grafts for use with -S; this is primarily to
* support git's cvsserver that wants to give a linear history
@@ -2086,7 +496,7 @@ static int read_ancestry(const char *graft_file)
return 0;
}
-static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
+static int update_auto_abbrev(int auto_abbrev, struct blame_origin *suspect)
{
const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
auto_abbrev);
@@ -2100,7 +510,7 @@ static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
* How many columns do we need to show line numbers, authors,
* and filenames?
*/
-static void find_alignment(struct scoreboard *sb, int *option)
+static void find_alignment(struct blame_scoreboard *sb, int *option)
{
int longest_src_lines = 0;
int longest_dst_lines = 0;
@@ -2110,7 +520,7 @@ static void find_alignment(struct scoreboard *sb, int *option)
int auto_abbrev = DEFAULT_ABBREV;
for (e = sb->ent; e; e = e->next) {
- struct origin *suspect = e->suspect;
+ struct blame_origin *suspect = e->suspect;
int num;
if (compute_auto_abbrev)
@@ -2138,8 +548,8 @@ static void find_alignment(struct scoreboard *sb, int *option)
num = e->lno + e->num_lines;
if (longest_dst_lines < num)
longest_dst_lines = num;
- if (largest_score < ent_score(sb, e))
- largest_score = ent_score(sb, e);
+ if (largest_score < blame_entry_score(sb, e))
+ largest_score = blame_entry_score(sb, e);
}
max_orig_digits = decimal_width(longest_src_lines);
max_digits = decimal_width(longest_dst_lines);
@@ -2150,31 +560,12 @@ static void find_alignment(struct scoreboard *sb, int *option)
abbrev = auto_abbrev + 1;
}
-/*
- * For debugging -- origin is refcounted, and this asserts that
- * we do not underflow.
- */
-static void sanity_check_refcnt(struct scoreboard *sb)
+static void sanity_check_on_fail(struct blame_scoreboard *sb, int baa)
{
- int baa = 0;
- struct blame_entry *ent;
-
- for (ent = sb->ent; ent; ent = ent->next) {
- /* Nobody should have zero or negative refcnt */
- if (ent->suspect->refcnt <= 0) {
- fprintf(stderr, "%s in %s has negative refcnt %d\n",
- ent->suspect->path,
- oid_to_hex(&ent->suspect->commit->object.oid),
- ent->suspect->refcnt);
- baa = 1;
- }
- }
- if (baa) {
- int opt = 0160;
- find_alignment(sb, &opt);
- output(sb, opt);
- die("Baa %d!", baa);
- }
+ int opt = OUTPUT_SHOW_SCORE | OUTPUT_SHOW_NUMBER | OUTPUT_SHOW_NAME;
+ find_alignment(sb, &opt);
+ output(sb, opt);
+ die("Baa %d!", baa);
}
static unsigned parse_score(const char *arg)
@@ -2224,301 +615,6 @@ static int git_blame_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
-static void verify_working_tree_path(struct commit *work_tree, const char *path)
-{
- struct commit_list *parents;
- int pos;
-
- for (parents = work_tree->parents; parents; parents = parents->next) {
- const struct object_id *commit_oid = &parents->item->object.oid;
- struct object_id blob_oid;
- unsigned mode;
-
- if (!get_tree_entry(commit_oid->hash, path, blob_oid.hash, &mode) &&
- sha1_object_info(blob_oid.hash, NULL) == OBJ_BLOB)
- return;
- }
-
- pos = cache_name_pos(path, strlen(path));
- if (pos >= 0)
- ; /* path is in the index */
- else if (-1 - pos < active_nr &&
- !strcmp(active_cache[-1 - pos]->name, path))
- ; /* path is in the index, unmerged */
- else
- die("no such path '%s' in HEAD", path);
-}
-
-static struct commit_list **append_parent(struct commit_list **tail, const struct object_id *oid)
-{
- struct commit *parent;
-
- parent = lookup_commit_reference(oid->hash);
- if (!parent)
- die("no such commit %s", oid_to_hex(oid));
- return &commit_list_insert(parent, tail)->next;
-}
-
-static void append_merge_parents(struct commit_list **tail)
-{
- int merge_head;
- struct strbuf line = STRBUF_INIT;
-
- merge_head = open(git_path_merge_head(), O_RDONLY);
- if (merge_head < 0) {
- if (errno == ENOENT)
- return;
- die("cannot open '%s' for reading", git_path_merge_head());
- }
-
- while (!strbuf_getwholeline_fd(&line, merge_head, '\n')) {
- struct object_id oid;
- if (line.len < GIT_SHA1_HEXSZ || get_oid_hex(line.buf, &oid))
- die("unknown line in '%s': %s", git_path_merge_head(), line.buf);
- tail = append_parent(tail, &oid);
- }
- close(merge_head);
- strbuf_release(&line);
-}
-
-/*
- * This isn't as simple as passing sb->buf and sb->len, because we
- * want to transfer ownership of the buffer to the commit (so we
- * must use detach).
- */
-static void set_commit_buffer_from_strbuf(struct commit *c, struct strbuf *sb)
-{
- size_t len;
- void *buf = strbuf_detach(sb, &len);
- set_commit_buffer(c, buf, len);
-}
-
-/*
- * 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 diff_options *opt,
- const char *path,
- const char *contents_from)
-{
- struct commit *commit;
- struct origin *origin;
- struct commit_list **parent_tail, *parent;
- struct object_id head_oid;
- struct strbuf buf = STRBUF_INIT;
- const char *ident;
- time_t now;
- int size, len;
- struct cache_entry *ce;
- unsigned mode;
- struct strbuf msg = STRBUF_INIT;
-
- read_cache();
- time(&now);
- commit = alloc_commit_node();
- commit->object.parsed = 1;
- commit->date = now;
- parent_tail = &commit->parents;
-
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
- die("no such ref: HEAD");
-
- parent_tail = append_parent(parent_tail, &head_oid);
- append_merge_parents(parent_tail);
- verify_working_tree_path(commit, path);
-
- origin = make_origin(commit, path);
-
- ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
- strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
- for (parent = commit->parents; parent; parent = parent->next)
- strbuf_addf(&msg, "parent %s\n",
- oid_to_hex(&parent->item->object.oid));
- strbuf_addf(&msg,
- "author %s\n"
- "committer %s\n\n"
- "Version of %s from %s\n",
- ident, ident, path,
- (!contents_from ? path :
- (!strcmp(contents_from, "-") ? "standard input" : contents_from)));
- set_commit_buffer_from_strbuf(commit, &msg);
-
- if (!contents_from || strcmp("-", contents_from)) {
- struct stat st;
- const char *read_from;
- char *buf_ptr;
- unsigned long buf_len;
-
- if (contents_from) {
- if (stat(contents_from, &st) < 0)
- die_errno("Cannot stat '%s'", contents_from);
- read_from = contents_from;
- }
- else {
- if (lstat(path, &st) < 0)
- die_errno("Cannot lstat '%s'", path);
- read_from = path;
- }
- mode = canon_mode(st.st_mode);
-
- switch (st.st_mode & S_IFMT) {
- case S_IFREG:
- if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
- textconv_object(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);
- break;
- case S_IFLNK:
- if (strbuf_readlink(&buf, read_from, st.st_size) < 0)
- die_errno("cannot readlink '%s'", read_from);
- break;
- default:
- die("unsupported file type %s", read_from);
- }
- }
- else {
- /* Reading from stdin */
- mode = 0;
- if (strbuf_read(&buf, 0, 0) < 0)
- die_errno("failed to read from stdin");
- }
- convert_to_git(path, buf.buf, buf.len, &buf, 0);
- origin->file.ptr = buf.buf;
- origin->file.size = buf.len;
- pretend_sha1_file(buf.buf, buf.len, OBJ_BLOB, origin->blob_oid.hash);
-
- /*
- * Read the current index, replace the path entry with
- * origin->blob_sha1 without mucking with its mode or type
- * bits; we are not going to write this index out -- we just
- * want to run "diff-index --cached".
- */
- discard_cache();
- read_cache();
-
- len = strlen(path);
- if (!mode) {
- int pos = cache_name_pos(path, len);
- if (0 <= pos)
- mode = active_cache[pos]->ce_mode;
- else
- /* Let's not bother reading from HEAD tree */
- mode = S_IFREG | 0644;
- }
- size = cache_entry_size(len);
- ce = xcalloc(1, size);
- 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_cache_entry(ce, ADD_CACHE_OK_TO_ADD|ADD_CACHE_OK_TO_REPLACE);
-
- cache_tree_invalidate_path(&the_index, path);
-
- return commit;
-}
-
-static struct commit *find_single_final(struct rev_info *revs,
- const char **name_p)
-{
- int i;
- struct commit *found = NULL;
- const char *name = NULL;
-
- for (i = 0; i < revs->pending.nr; i++) {
- struct object *obj = revs->pending.objects[i].item;
- if (obj->flags & UNINTERESTING)
- continue;
- obj = deref_tag(obj, NULL, 0);
- if (obj->type != OBJ_COMMIT)
- die("Non commit %s?", revs->pending.objects[i].name);
- if (found)
- die("More than one commit to dig from %s and %s?",
- revs->pending.objects[i].name, name);
- found = (struct commit *)obj;
- name = revs->pending.objects[i].name;
- }
- if (name_p)
- *name_p = name;
- return found;
-}
-
-static char *prepare_final(struct scoreboard *sb)
-{
- const char *name;
- sb->final = find_single_final(sb->revs, &name);
- return xstrdup_or_null(name);
-}
-
-static const char *dwim_reverse_initial(struct scoreboard *sb)
-{
- /*
- * DWIM "git blame --reverse ONE -- PATH" as
- * "git blame --reverse ONE..HEAD -- PATH" but only do so
- * when it makes sense.
- */
- struct object *obj;
- struct commit *head_commit;
- unsigned char head_sha1[20];
-
- if (sb->revs->pending.nr != 1)
- return NULL;
-
- /* Is that sole rev a committish? */
- obj = sb->revs->pending.objects[0].item;
- obj = deref_tag(obj, NULL, 0);
- if (obj->type != OBJ_COMMIT)
- return NULL;
-
- /* Do we have HEAD? */
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
- return NULL;
- head_commit = lookup_commit_reference_gently(head_sha1, 1);
- if (!head_commit)
- return NULL;
-
- /* Turn "ONE" into "ONE..HEAD" then */
- obj->flags |= UNINTERESTING;
- add_pending_object(sb->revs, &head_commit->object, "HEAD");
-
- sb->final = (struct commit *)obj;
- return sb->revs->pending.objects[0].name;
-}
-
-static char *prepare_initial(struct scoreboard *sb)
-{
- int i;
- const char *final_commit_name = NULL;
- struct rev_info *revs = sb->revs;
-
- /*
- * There must be one and only one negative commit, and it must be
- * the boundary.
- */
- for (i = 0; i < revs->pending.nr; i++) {
- struct object *obj = revs->pending.objects[i].item;
- if (!(obj->flags & UNINTERESTING))
- continue;
- obj = deref_tag(obj, NULL, 0);
- if (obj->type != OBJ_COMMIT)
- die("Non commit %s?", revs->pending.objects[i].name);
- if (sb->final)
- die("More than one commit to dig up from, %s and %s?",
- revs->pending.objects[i].name,
- final_commit_name);
- sb->final = (struct commit *) obj;
- final_commit_name = revs->pending.objects[i].name;
- }
-
- if (!final_commit_name)
- final_commit_name = dwim_reverse_initial(sb);
- if (!final_commit_name)
- die("No commit to dig up from?");
- return xstrdup(final_commit_name);
-}
-
static int blame_copy_callback(const struct option *option, const char *arg, int unset)
{
int *opt = option->value;
@@ -2556,13 +652,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
{
struct rev_info revs;
const char *path;
- struct scoreboard sb;
- struct origin *o;
+ struct blame_scoreboard sb;
+ struct blame_origin *o;
struct blame_entry *ent = NULL;
long dashdash_pos, lno;
- char *final_commit_name = NULL;
- enum object_type type;
- struct commit *final_commit = NULL;
+ struct progress_info pi = { NULL, 0 };
struct string_list range_list = STRING_LIST_INIT_NODUP;
int output_option = 0, opt = 0;
@@ -2712,11 +806,6 @@ parse_done:
opt |= (PICKAXE_BLAME_COPY | PICKAXE_BLAME_MOVE |
PICKAXE_BLAME_COPY_HARDER);
- if (!blame_move_score)
- blame_move_score = BLAME_DEFAULT_MOVE_SCORE;
- if (!blame_copy_score)
- blame_copy_score = BLAME_DEFAULT_COPY_SCORE;
-
/*
* We have collected options unknown to us in argv[1..unk]
* which are to be passed to revision machinery if we are
@@ -2769,94 +858,13 @@ parse_done:
revs.disable_stdin = 1;
setup_revisions(argc, argv, &revs, NULL);
- memset(&sb, 0, sizeof(sb));
+ init_scoreboard(&sb);
sb.revs = &revs;
- if (!reverse) {
- final_commit_name = prepare_final(&sb);
- sb.commits.compare = compare_commits_by_commit_date;
- }
- else if (contents_from)
- die(_("--contents and --reverse do not blend well."));
- else {
- final_commit_name = prepare_initial(&sb);
- sb.commits.compare = compare_commits_by_reverse_commit_date;
- if (revs.first_parent_only)
- revs.children.name = NULL;
- }
-
- if (!sb.final) {
- /*
- * "--not A B -- path" without anything positive;
- * do not default to HEAD, but use the working tree
- * or "--contents".
- */
- setup_work_tree();
- sb.final = fake_working_tree_commit(&sb.revs->diffopt,
- path, contents_from);
- add_pending_object(&revs, &(sb.final->object), ":");
- }
- else if (contents_from)
- die(_("cannot use --contents with final commit object name"));
-
- if (reverse && revs.first_parent_only) {
- final_commit = find_single_final(sb.revs, NULL);
- if (!final_commit)
- die(_("--reverse and --first-parent together require specified latest commit"));
- }
-
- /*
- * If we have bottom, this will mark the ancestors of the
- * bottom commits we would reach while traversing as
- * uninteresting.
- */
- if (prepare_revision_walk(&revs))
- die(_("revision walk setup failed"));
-
- if (reverse && revs.first_parent_only) {
- struct commit *c = final_commit;
-
- sb.revs->children.name = "children";
- while (c->parents &&
- oidcmp(&c->object.oid, &sb.final->object.oid)) {
- struct commit_list *l = xcalloc(1, sizeof(*l));
-
- l->item = c;
- if (add_decoration(&sb.revs->children,
- &c->parents->item->object, l))
- die("BUG: not unique item in first-parent chain");
- c = c->parents->item;
- }
-
- if (oidcmp(&c->object.oid, &sb.final->object.oid))
- die(_("--reverse --first-parent together require range along first-parent chain"));
- }
-
- if (is_null_oid(&sb.final->object.oid)) {
- o = sb.final->util;
- sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
- sb.final_buf_size = o->file.size;
- }
- else {
- o = get_origin(&sb, sb.final, path);
- if (fill_blob_sha1_and_mode(o))
- die(_("no such path %s in %s"), path, final_commit_name);
-
- if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
- textconv_object(path, o->mode, &o->blob_oid, 1, (char **) &sb.final_buf,
- &sb.final_buf_size))
- ;
- else
- sb.final_buf = read_sha1_file(o->blob_oid.hash, &type,
- &sb.final_buf_size);
-
- if (!sb.final_buf)
- die(_("cannot read blob %s for path %s"),
- oid_to_hex(&o->blob_oid),
- path);
- }
- num_read_blob++;
- lno = prepare_lines(&sb);
+ sb.contents_from = contents_from;
+ sb.reverse = reverse;
+ setup_scoreboard(&sb, path, &o);
+ lno = sb.num_lines;
if (lno && !range_list.nr)
string_list_append(&range_list, "1");
@@ -2885,22 +893,13 @@ parse_done:
for (range_i = ranges.nr; range_i > 0; --range_i) {
const struct range *r = &ranges.ranges[range_i - 1];
- long bottom = r->start;
- long top = r->end;
- struct blame_entry *next = ent;
- ent = xcalloc(1, sizeof(*ent));
- ent->lno = bottom;
- ent->num_lines = top - bottom;
- ent->suspect = o;
- ent->s_lno = bottom;
- ent->next = next;
- origin_incref(o);
+ ent = blame_entry_prepend(ent, r->start, r->end, o);
}
o->suspects = ent;
prio_queue_put(&sb.commits, o->commit);
- origin_decref(o);
+ blame_origin_decref(o);
range_set_release(&ranges);
string_list_clear(&range_list, 0);
@@ -2908,21 +907,38 @@ parse_done:
sb.ent = NULL;
sb.path = path;
+ if (blame_move_score)
+ sb.move_score = blame_move_score;
+ if (blame_copy_score)
+ sb.copy_score = blame_copy_score;
+
+ sb.debug = DEBUG;
+ sb.on_sanity_fail = &sanity_check_on_fail;
+
+ sb.show_root = show_root;
+ sb.xdl_opts = xdl_opts;
+ sb.no_whole_file_rename = no_whole_file_rename;
+
read_mailmap(&mailmap, NULL);
+ sb.found_guilty_entry = &found_guilty_entry;
+ sb.found_guilty_entry_data = &pi;
+ if (show_progress)
+ pi.progress = start_progress_delay(_("Blaming lines"),
+ sb.num_lines, 50, 1);
+
assign_blame(&sb, opt);
+ stop_progress(&pi.progress);
+
if (!incremental)
setup_pager();
-
- free(final_commit_name);
-
- if (incremental)
+ else
return 0;
- sb.ent = blame_sort(sb.ent, compare_blame_final);
+ blame_sort_final(&sb);
- coalesce(&sb);
+ blame_coalesce(&sb);
if (!(output_option & OUTPUT_PORCELAIN))
find_alignment(&sb, &output_option);
@@ -2936,9 +952,9 @@ parse_done:
}
if (show_stats) {
- printf("num read blob: %d\n", num_read_blob);
- printf("num get patch: %d\n", num_get_patch);
- printf("num commits: %d\n", num_commits);
+ printf("num read blob: %d\n", sb.num_read_blob);
+ printf("num get patch: %d\n", sb.num_get_patch);
+ printf("num commits: %d\n", sb.num_commits);
}
return 0;
}
diff --git a/builtin/branch.c b/builtin/branch.c
index 48a513a84d..83fcda43dc 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -124,7 +124,7 @@ static int branch_merged(int kind, const char *name,
(reference_name = reference_name_to_free =
resolve_refdup(upstream, RESOLVE_REF_READING,
oid.hash, NULL)) != NULL)
- reference_rev = lookup_commit_reference(oid.hash);
+ reference_rev = lookup_commit_reference(&oid);
}
if (!reference_rev)
reference_rev = head_rev;
@@ -157,7 +157,7 @@ static int check_branch_commit(const char *branchname, const char *refname,
const struct object_id *oid, struct commit *head_rev,
int kinds, int force)
{
- struct commit *rev = lookup_commit_reference(oid->hash);
+ struct commit *rev = lookup_commit_reference(oid);
if (!rev) {
error(_("Couldn't look up commit object for '%s'"), refname);
return -1;
@@ -211,7 +211,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
}
if (!force) {
- head_rev = lookup_commit_reference(head_oid.hash);
+ head_rev = lookup_commit_reference(&head_oid);
if (!head_rev)
die(_("Couldn't look up commit object for HEAD"));
}
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 9af863e791..4bffd7a2d8 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -5,6 +5,7 @@
*/
#include "cache.h"
#include "builtin.h"
+#include "diff.h"
#include "parse-options.h"
#include "userdiff.h"
#include "streaming.h"
@@ -61,7 +62,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
if (unknown_type)
flags |= LOOKUP_UNKNOWN_OBJECT;
- if (get_sha1_with_context(obj_name, 0, oid.hash, &obj_context))
+ if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
+ oid.hash, &obj_context))
die("Not a valid object name %s", obj_name);
if (!path)
@@ -166,6 +168,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
write_or_die(1, buf, size);
free(buf);
+ free(obj_context.path);
return 0;
}
diff --git a/builtin/check-ignore.c b/builtin/check-ignore.c
index 1d73d3ca3d..c7b8c08897 100644
--- a/builtin/check-ignore.c
+++ b/builtin/check-ignore.c
@@ -4,6 +4,7 @@
#include "quote.h"
#include "pathspec.h"
#include "parse-options.h"
+#include "submodule.h"
static int quiet, verbose, stdin_paths, show_non_matching, no_index;
static const char * const check_ignore_usage[] = {
@@ -87,21 +88,23 @@ static int check_ignore(struct dir_struct *dir,
parse_pathspec(&pathspec,
PATHSPEC_ALL_MAGIC & ~PATHSPEC_FROMTOP,
PATHSPEC_SYMLINK_LEADING_PATH |
- PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE |
PATHSPEC_KEEP_ORDER,
prefix, argv);
+ die_path_inside_submodule(&the_index, &pathspec);
+
/*
* look for pathspecs matching entries in the index, since these
* should not be ignored, in order to be consistent with
* 'git status', 'git add' etc.
*/
- seen = find_pathspecs_matching_against_index(&pathspec);
+ seen = find_pathspecs_matching_against_index(&pathspec, &the_index);
for (i = 0; i < pathspec.nr; i++) {
full_path = pathspec.items[i].match;
exclude = NULL;
if (!seen[i]) {
- exclude = last_exclude_matching(dir, full_path, &dtype);
+ exclude = last_exclude_matching(dir, &the_index,
+ full_path, &dtype);
}
if (!quiet && (exclude || show_non_matching))
output_exclude(pathspec.items[i].original, exclude);
diff --git a/builtin/checkout.c b/builtin/checkout.c
index b360943455..a6b2af39d3 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -395,7 +395,7 @@ static int checkout_paths(const struct checkout_opts *opts,
die(_("unable to write new index file"));
read_ref_full("HEAD", 0, rev.hash, NULL);
- head = lookup_commit_reference_gently(rev.hash, 1);
+ head = lookup_commit_reference_gently(&rev, 1);
errs |= post_checkout_hook(head, head, 0);
return errs;
@@ -529,10 +529,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
setup_standard_excludes(topts.dir);
}
tree = parse_tree_indirect(old->commit ?
- old->commit->object.oid.hash :
- EMPTY_TREE_SHA1_BIN);
+ &old->commit->object.oid :
+ &empty_tree_oid);
init_tree_desc(&trees[0], tree->buffer, tree->size);
- tree = parse_tree_indirect(new->commit->object.oid.hash);
+ tree = parse_tree_indirect(&new->commit->object.oid);
init_tree_desc(&trees[1], tree->buffer, tree->size);
ret = unpack_trees(2, trees, &topts);
@@ -723,7 +723,7 @@ static int add_pending_uninteresting_ref(const char *refname,
const struct object_id *oid,
int flags, void *cb_data)
{
- add_pending_sha1(cb_data, refname, oid->hash, UNINTERESTING);
+ add_pending_oid(cb_data, refname, oid, UNINTERESTING);
return 0;
}
@@ -809,7 +809,7 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
add_pending_object(&revs, object, oid_to_hex(&object->oid));
for_each_ref(add_pending_uninteresting_ref, &revs);
- add_pending_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING);
+ add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
refs = revs.pending;
revs.leak_pending = 1;
@@ -836,7 +836,7 @@ static int switch_branches(const struct checkout_opts *opts,
memset(&old, 0, sizeof(old));
old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
if (old.path)
- old.commit = lookup_commit_reference_gently(rev.hash, 1);
+ old.commit = lookup_commit_reference_gently(&rev, 1);
if (!(flag & REF_ISSYMREF))
old.path = NULL;
@@ -1050,10 +1050,10 @@ static int parse_branchname_arg(int argc, const char **argv,
else
new->path = NULL; /* not an existing branch */
- new->commit = lookup_commit_reference_gently(rev->hash, 1);
+ new->commit = lookup_commit_reference_gently(rev, 1);
if (!new->commit) {
/* not a commit */
- *source_tree = parse_tree_indirect(rev->hash);
+ *source_tree = parse_tree_indirect(rev);
} else {
parse_commit_or_die(new->commit);
*source_tree = new->commit->tree;
diff --git a/builtin/clean.c b/builtin/clean.c
index d861f836a2..142bf668cf 100644
--- a/builtin/clean.c
+++ b/builtin/clean.c
@@ -683,7 +683,7 @@ static int filter_by_patterns_cmd(void)
for_each_string_list_item(item, &del_list) {
int dtype = DT_UNKNOWN;
- if (is_excluded(&dir, item->string, &dtype)) {
+ if (is_excluded(&dir, &the_index, item->string, &dtype)) {
*item->string = '\0';
changed++;
}
@@ -857,6 +857,38 @@ static void interactive_main_loop(void)
}
}
+static void correct_untracked_entries(struct dir_struct *dir)
+{
+ int src, dst, ign;
+
+ for (src = dst = ign = 0; src < dir->nr; src++) {
+ /* skip paths in ignored[] that cannot be inside entries[src] */
+ while (ign < dir->ignored_nr &&
+ 0 <= cmp_dir_entry(&dir->entries[src], &dir->ignored[ign]))
+ ign++;
+
+ if (ign < dir->ignored_nr &&
+ check_dir_entry_contains(dir->entries[src], dir->ignored[ign])) {
+ /* entries[src] contains an ignored path, so we drop it */
+ free(dir->entries[src]);
+ } else {
+ struct dir_entry *ent = dir->entries[src++];
+
+ /* entries[src] does not contain an ignored path, so we keep it */
+ dir->entries[dst++] = ent;
+
+ /* then discard paths in entries[] contained inside entries[src] */
+ while (src < dir->nr &&
+ check_dir_entry_contains(ent, dir->entries[src]))
+ free(dir->entries[src++]);
+
+ /* compensate for the outer loop's loop control */
+ src--;
+ }
+ }
+ dir->nr = dst;
+}
+
int cmd_clean(int argc, const char **argv, const char *prefix)
{
int i, res;
@@ -916,6 +948,9 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
dir.flags |= DIR_SHOW_OTHER_DIRECTORIES;
+ if (remove_directories)
+ dir.flags |= DIR_SHOW_IGNORED_TOO | DIR_KEEP_UNTRACKED_CONTENTS;
+
if (read_cache() < 0)
die(_("index file corrupt"));
@@ -930,7 +965,8 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
PATHSPEC_PREFER_CWD,
prefix, argv);
- fill_directory(&dir, &pathspec);
+ fill_directory(&dir, &the_index, &pathspec);
+ correct_untracked_entries(&dir);
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
@@ -958,6 +994,12 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
string_list_append(&del_list, rel);
}
+ for (i = 0; i < dir.nr; i++)
+ free(dir.entries[i]);
+
+ for (i = 0; i < dir.ignored_nr; i++)
+ free(dir.ignored[i]);
+
if (interactive && del_list.nr > 0)
interactive_main_loop();
diff --git a/builtin/clone.c b/builtin/clone.c
index a6ae7d6180..743f16ae2a 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -40,6 +40,7 @@ static const char * const builtin_clone_usage[] = {
static int option_no_checkout, option_bare, option_mirror, option_single_branch = -1;
static int option_local = -1, option_no_hardlinks, option_shared;
+static int option_no_tags;
static int option_shallow_submodules;
static int deepen;
static char *option_template, *option_depth, *option_since;
@@ -120,6 +121,8 @@ static struct option builtin_clone_options[] = {
N_("deepen history of shallow clone, excluding rev")),
OPT_BOOL(0, "single-branch", &option_single_branch,
N_("clone only one branch, HEAD or --branch")),
+ OPT_BOOL(0, "no-tags", &option_no_tags,
+ N_("don't clone any tags, and make later fetches not to follow them")),
OPT_BOOL(0, "shallow-submodules", &option_shallow_submodules,
N_("any cloned submodules will be shallow")),
OPT_STRING(0, "separate-git-dir", &real_git_dir, N_("gitdir"),
@@ -563,7 +566,7 @@ static struct ref *wanted_peer_refs(const struct ref *refs,
} else
get_fetch_map(refs, refspec, &tail, 0);
- if (!option_mirror && !option_single_branch)
+ if (!option_mirror && !option_single_branch && !option_no_tags)
get_fetch_map(refs, tag_refspec, &tail, 0);
return local_refs;
@@ -652,7 +655,7 @@ static void update_remote_refs(const struct ref *refs,
if (refs) {
write_remote_refs(mapped_refs);
- if (option_single_branch)
+ if (option_single_branch && !option_no_tags)
write_followtags(refs, msg);
}
@@ -682,7 +685,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
install_branch_config(0, head, option_origin, our->name);
}
} else if (our) {
- struct commit *c = lookup_commit_reference(our->old_oid.hash);
+ struct commit *c = lookup_commit_reference(&our->old_oid);
/* --branch specifies a non-branch (i.e. tags), detach HEAD */
update_ref(msg, "HEAD", c->object.oid.hash,
NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
@@ -739,7 +742,7 @@ static int checkout(int submodule_progress)
opts.src_index = &the_index;
opts.dst_index = &the_index;
- tree = parse_tree_indirect(oid.hash);
+ tree = parse_tree_indirect(&oid);
parse_tree(tree);
init_tree_desc(&t, tree->buffer, tree->size);
if (unpack_trees(1, &t, &opts) < 0)
@@ -1037,6 +1040,12 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
git_config_set(key.buf, repo);
strbuf_reset(&key);
+ if (option_no_tags) {
+ strbuf_addf(&key, "remote.%s.tagOpt", option_origin);
+ git_config_set(key.buf, "--no-tags");
+ strbuf_reset(&key);
+ }
+
if (option_required_reference.nr || option_optional_reference.nr)
setup_reference();
diff --git a/builtin/commit-tree.c b/builtin/commit-tree.c
index 605017261c..f39c2b2737 100644
--- a/builtin/commit-tree.c
+++ b/builtin/commit-tree.c
@@ -58,7 +58,7 @@ int cmd_commit_tree(int argc, const char **argv, const char *prefix)
if (get_sha1_commit(argv[i], oid.hash))
die("Not a valid object name %s", argv[i]);
assert_sha1_type(oid.hash, OBJ_COMMIT);
- new_parent(lookup_commit(oid.hash), &parents);
+ new_parent(lookup_commit(&oid), &parents);
continue;
}
diff --git a/builtin/commit.c b/builtin/commit.c
index 8d1cac0629..da1ba4c862 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -313,7 +313,7 @@ static void create_base_index(const struct commit *current_head)
opts.dst_index = &the_index;
opts.fn = oneway_merge;
- tree = parse_tree_indirect(current_head->object.oid.hash);
+ tree = parse_tree_indirect(&current_head->object.oid);
if (!tree)
die(_("failed to unpack HEAD tree object"));
parse_tree(tree);
@@ -1263,6 +1263,10 @@ static int parse_status_slot(const char *slot)
return WT_STATUS_NOBRANCH;
if (!strcasecmp(slot, "unmerged"))
return WT_STATUS_UNMERGED;
+ if (!strcasecmp(slot, "localBranch"))
+ return WT_STATUS_LOCAL_BRANCH;
+ if (!strcasecmp(slot, "remoteBranch"))
+ return WT_STATUS_REMOTE_BRANCH;
return -1;
}
@@ -1430,7 +1434,7 @@ static void print_summary(const char *prefix, const struct object_id *oid,
struct strbuf author_ident = STRBUF_INIT;
struct strbuf committer_ident = STRBUF_INIT;
- commit = lookup_commit(oid->hash);
+ commit = lookup_commit(oid);
if (!commit)
die(_("couldn't look up newly created commit"));
if (parse_commit(commit))
@@ -1654,7 +1658,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
if (get_sha1("HEAD", oid.hash))
current_head = NULL;
else {
- current_head = lookup_commit_or_die(oid.hash, "HEAD");
+ current_head = lookup_commit_or_die(&oid, "HEAD");
if (parse_commit(current_head))
die(_("could not parse HEAD commit"));
}
@@ -1758,7 +1762,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
append_merge_tag_headers(parents, &tail);
}
- if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
+ if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->oid.hash,
parents, oid.hash, author_ident.buf, sign_commit, extra)) {
rollback_index_files();
die(_("failed to write commit object"));
diff --git a/builtin/config.c b/builtin/config.c
index 3a554ad50c..7f6c25d4d9 100644
--- a/builtin/config.c
+++ b/builtin/config.c
@@ -496,6 +496,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
usage_with_options(builtin_config_usage, builtin_config_options);
}
+ if (use_local_config && nongit)
+ die(_("--local can only be used inside a git repository"));
+
if (given_config_source.file &&
!strcmp(given_config_source.file, "-")) {
given_config_source.file = NULL;
diff --git a/builtin/describe.c b/builtin/describe.c
index a5cd8c513f..893c8789f4 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -79,13 +79,13 @@ static int replace_name(struct commit_name *e,
struct tag *t;
if (!e->tag) {
- t = lookup_tag(e->oid.hash);
+ t = lookup_tag(&e->oid);
if (!t || parse_tag(t))
return 1;
e->tag = t;
}
- t = lookup_tag(oid->hash);
+ t = lookup_tag(oid);
if (!t || parse_tag(t))
return 0;
*tag = t;
@@ -245,7 +245,7 @@ static unsigned long finish_depth_computation(
static void display_name(struct commit_name *n)
{
if (n->prio == 2 && !n->tag) {
- n->tag = lookup_tag(n->oid.hash);
+ n->tag = lookup_tag(&n->oid);
if (!n->tag || parse_tag(n->tag))
die(_("annotated tag %s not available"), n->path);
}
@@ -281,7 +281,7 @@ static void describe(const char *arg, int last_one)
if (get_oid(arg, &oid))
die(_("Not a valid object name %s"), arg);
- cmit = lookup_commit_reference(oid.hash);
+ cmit = lookup_commit_reference(&oid);
if (!cmit)
die(_("%s is not a valid '%s' object"), arg, commit_type);
@@ -309,7 +309,7 @@ static void describe(const char *arg, int last_one)
struct commit *c;
struct commit_name *n = hashmap_iter_first(&names, &iter);
for (; n; n = hashmap_iter_next(&iter)) {
- c = lookup_commit_reference_gently(n->peeled.hash, 1);
+ c = lookup_commit_reference_gently(&n->peeled, 1);
if (c)
c->util = n;
}
diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 15c61fd8d1..a572da9d51 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -20,9 +20,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
int result;
unsigned options = 0;
+ git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
init_revisions(&rev, prefix);
gitmodules_config();
- git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
precompose_argv(argc, argv);
diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 1af373d002..f084826a29 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -17,9 +17,9 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
int i;
int result;
+ git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
init_revisions(&rev, prefix);
gitmodules_config();
- git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
rev.abbrev = 0;
precompose_argv(argc, argv);
diff --git a/builtin/diff-tree.c b/builtin/diff-tree.c
index 326f88b657..e401112045 100644
--- a/builtin/diff-tree.c
+++ b/builtin/diff-tree.c
@@ -9,7 +9,7 @@ static struct rev_info log_tree_opt;
static int diff_tree_commit_sha1(const struct object_id *oid)
{
- struct commit *commit = lookup_commit_reference(oid->hash);
+ struct commit *commit = lookup_commit_reference(oid);
if (!commit)
return -1;
return log_tree_commit(&log_tree_opt, commit);
@@ -23,7 +23,7 @@ static int stdin_diff_commit(struct commit *commit, const char *p)
/* Graft the fake parents locally to the commit */
while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
- struct commit *parent = lookup_commit(oid.hash);
+ struct commit *parent = lookup_commit(&oid);
if (!pptr) {
/* Free the real parent list */
free_commit_list(commit->parents);
@@ -44,7 +44,7 @@ static int stdin_diff_trees(struct tree *tree1, const char *p)
struct tree *tree2;
if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
return error("Need exactly two trees, separated by a space");
- tree2 = lookup_tree(oid.hash);
+ tree2 = lookup_tree(&oid);
if (!tree2 || parse_tree(tree2))
return -1;
printf("%s %s\n", oid_to_hex(&tree1->object.oid),
@@ -67,7 +67,7 @@ static int diff_tree_stdin(char *line)
line[len-1] = 0;
if (parse_oid_hex(line, &oid, &p))
return -1;
- obj = parse_object(oid.hash);
+ obj = parse_object(&oid);
if (!obj)
return -1;
if (obj->type == OBJ_COMMIT)
@@ -105,9 +105,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
struct setup_revision_opt s_r_opt;
int read_stdin = 0;
+ git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
init_revisions(opt, prefix);
gitmodules_config();
- git_config(git_diff_basic_config, NULL); /* no "diff" UI options */
opt->abbrev = 0;
opt->diff = 1;
opt->disable_stdin = 1;
diff --git a/builtin/diff.c b/builtin/diff.c
index d184aafab9..0c8f86e40d 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -20,23 +20,22 @@
#define DIFF_NO_INDEX_EXPLICIT 1
#define DIFF_NO_INDEX_IMPLICIT 2
-struct blobinfo {
- struct object_id oid;
- const char *name;
- unsigned mode;
-};
-
static const char builtin_diff_usage[] =
"git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
+static const char *blob_path(struct object_array_entry *entry)
+{
+ return entry->path ? entry->path : entry->name;
+}
+
static void stuff_change(struct diff_options *opt,
unsigned old_mode, unsigned new_mode,
const struct object_id *old_oid,
const struct object_id *new_oid,
int old_oid_valid,
int new_oid_valid,
- const char *old_name,
- const char *new_name)
+ const char *old_path,
+ const char *new_path)
{
struct diff_filespec *one, *two;
@@ -47,16 +46,16 @@ static void stuff_change(struct diff_options *opt,
if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
SWAP(old_mode, new_mode);
SWAP(old_oid, new_oid);
- SWAP(old_name, new_name);
+ SWAP(old_path, new_path);
}
if (opt->prefix &&
- (strncmp(old_name, opt->prefix, opt->prefix_length) ||
- strncmp(new_name, opt->prefix, opt->prefix_length)))
+ (strncmp(old_path, opt->prefix, opt->prefix_length) ||
+ strncmp(new_path, opt->prefix, opt->prefix_length)))
return;
- one = alloc_filespec(old_name);
- two = alloc_filespec(new_name);
+ one = alloc_filespec(old_path);
+ two = alloc_filespec(new_path);
fill_filespec(one, old_oid->hash, old_oid_valid, old_mode);
fill_filespec(two, new_oid->hash, new_oid_valid, new_mode);
@@ -65,7 +64,7 @@ static void stuff_change(struct diff_options *opt,
static int builtin_diff_b_f(struct rev_info *revs,
int argc, const char **argv,
- struct blobinfo *blob)
+ struct object_array_entry **blob)
{
/* Blob vs file in the working tree*/
struct stat st;
@@ -84,14 +83,15 @@ static int builtin_diff_b_f(struct rev_info *revs,
diff_set_mnemonic_prefix(&revs->diffopt, "o/", "w/");
- if (blob[0].mode == S_IFINVALID)
- blob[0].mode = canon_mode(st.st_mode);
+ if (blob[0]->mode == S_IFINVALID)
+ blob[0]->mode = canon_mode(st.st_mode);
stuff_change(&revs->diffopt,
- blob[0].mode, canon_mode(st.st_mode),
- &blob[0].oid, &null_oid,
+ blob[0]->mode, canon_mode(st.st_mode),
+ &blob[0]->item->oid, &null_oid,
1, 0,
- path, path);
+ blob[0]->path ? blob[0]->path : path,
+ path);
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
return 0;
@@ -99,24 +99,24 @@ static int builtin_diff_b_f(struct rev_info *revs,
static int builtin_diff_blobs(struct rev_info *revs,
int argc, const char **argv,
- struct blobinfo *blob)
+ struct object_array_entry **blob)
{
unsigned mode = canon_mode(S_IFREG | 0644);
if (argc > 1)
usage(builtin_diff_usage);
- if (blob[0].mode == S_IFINVALID)
- blob[0].mode = mode;
+ if (blob[0]->mode == S_IFINVALID)
+ blob[0]->mode = mode;
- if (blob[1].mode == S_IFINVALID)
- blob[1].mode = mode;
+ if (blob[1]->mode == S_IFINVALID)
+ blob[1]->mode = mode;
stuff_change(&revs->diffopt,
- blob[0].mode, blob[1].mode,
- &blob[0].oid, &blob[1].oid,
+ blob[0]->mode, blob[1]->mode,
+ &blob[0]->item->oid, &blob[1]->item->oid,
1, 1,
- blob[0].name, blob[1].name);
+ blob_path(blob[0]), blob_path(blob[1]));
diffcore_std(&revs->diffopt);
diff_flush(&revs->diffopt);
return 0;
@@ -259,7 +259,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
struct rev_info rev;
struct object_array ent = OBJECT_ARRAY_INIT;
int blobs = 0, paths = 0;
- struct blobinfo blob[2];
+ struct object_array_entry *blob[2];
int nongit = 0, no_index = 0;
int result = 0;
@@ -381,7 +381,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
add_head_to_pending(&rev);
if (!rev.pending.nr) {
struct tree *tree;
- tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+ tree = lookup_tree(&empty_tree_oid);
add_pending_object(&rev, &tree->object, "HEAD");
}
break;
@@ -395,7 +395,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
const char *name = entry->name;
int flags = (obj->flags & UNINTERESTING);
if (!obj->parsed)
- obj = parse_object(obj->oid.hash);
+ obj = parse_object(&obj->oid);
obj = deref_tag(obj, NULL, 0);
if (!obj)
die(_("invalid object '%s' given."), name);
@@ -408,9 +408,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
} else if (obj->type == OBJ_BLOB) {
if (2 <= blobs)
die(_("more than two blobs given: '%s'"), name);
- hashcpy(blob[blobs].oid.hash, obj->oid.hash);
- blob[blobs].name = name;
- blob[blobs].mode = entry->mode;
+ blob[blobs] = entry;
blobs++;
} else {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 64617ad8e3..24e29ad7ea 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -232,7 +232,7 @@ static void export_blob(const struct object_id *oid)
if (anonymize) {
buf = anonymize_blob(&size);
- object = (struct object *)lookup_blob(oid->hash);
+ object = (struct object *)lookup_blob(oid);
eaten = 0;
} else {
buf = read_sha1_file(oid->hash, &type, &size);
@@ -240,7 +240,7 @@ static void export_blob(const struct object_id *oid)
die ("Could not read blob %s", oid_to_hex(oid));
if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0)
die("sha1 mismatch in blob %s", oid_to_hex(oid));
- object = parse_object_buffer(oid->hash, type, size, buf, &eaten);
+ object = parse_object_buffer(oid, type, size, buf, &eaten);
}
if (!object)
@@ -779,7 +779,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
/* handle nested tags */
while (tag && tag->object.type == OBJ_TAG) {
- parse_object(tag->object.oid.hash);
+ parse_object(&tag->object.oid);
string_list_append(&extra_refs, full_name)->util = tag;
tag = (struct tag *)tag->tagged;
}
@@ -940,7 +940,7 @@ static void import_marks(char *input_file)
/* only commits */
continue;
- commit = lookup_commit(oid.hash);
+ commit = lookup_commit(&oid);
if (!commit)
die("not a commit? can't happen: %s", oid_to_hex(&oid));
diff --git a/builtin/fetch.c b/builtin/fetch.c
index 5f2c2ab23e..47708451bc 100644
--- a/builtin/fetch.c
+++ b/builtin/fetch.c
@@ -636,8 +636,8 @@ static int update_local_ref(struct ref *ref,
return r;
}
- current = lookup_commit_reference_gently(ref->old_oid.hash, 1);
- updated = lookup_commit_reference_gently(ref->new_oid.hash, 1);
+ current = lookup_commit_reference_gently(&ref->old_oid, 1);
+ updated = lookup_commit_reference_gently(&ref->new_oid, 1);
if (!current || !updated) {
const char *msg;
const char *what;
@@ -770,7 +770,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
continue;
}
- commit = lookup_commit_reference_gently(rm->old_oid.hash, 1);
+ commit = lookup_commit_reference_gently(&rm->old_oid,
+ 1);
if (!commit)
rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
@@ -940,7 +941,7 @@ static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map,
for (ref = stale_refs; ref; ref = ref->next)
string_list_append(&refnames, ref->name);
- result = delete_refs(&refnames, 0);
+ result = delete_refs("fetch: prune", &refnames, 0);
string_list_clear(&refnames, 0);
}
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index 6faa3c0d24..70137b0b7e 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -341,7 +341,7 @@ static void shortlog(const char *name,
const struct object_id *oid = &origin_data->oid;
int limit = opts->shortlog_len;
- branch = deref_tag(parse_object(oid->hash), oid_to_hex(oid), GIT_SHA1_HEXSZ);
+ branch = deref_tag(parse_object(oid), oid_to_hex(oid), GIT_SHA1_HEXSZ);
if (!branch || branch->type != OBJ_COMMIT)
return;
@@ -559,14 +559,14 @@ static void find_merge_parents(struct merge_parents *result,
* "name" here and we do not want to contaminate its
* util field yet.
*/
- obj = parse_object(oid.hash);
+ obj = parse_object(&oid);
parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
if (!parent)
continue;
commit_list_insert(parent, &parents);
add_merge_parent(result, &obj->oid, &parent->object.oid);
}
- head_commit = lookup_commit(head->hash);
+ head_commit = lookup_commit(head);
if (head_commit)
commit_list_insert(head_commit, &parents);
parents = reduce_heads(parents);
@@ -633,7 +633,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
struct commit *head;
struct rev_info rev;
- head = lookup_commit_or_die(head_oid.hash, "HEAD");
+ head = lookup_commit_or_die(&head_oid, "HEAD");
init_revisions(&rev, NULL);
rev.commit_format = CMIT_FMT_ONELINE;
rev.ignore_merges = 1;
diff --git a/builtin/fsck.c b/builtin/fsck.c
index b5e13a4556..cb2ba6cd1b 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -377,7 +377,7 @@ static int fsck_obj(struct object *obj)
return 0;
}
-static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
+static int fsck_obj_buffer(const struct object_id *oid, enum object_type type,
unsigned long size, void *buffer, int *eaten)
{
/*
@@ -385,10 +385,10 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
* verify_packfile(), data_valid variable for details.
*/
struct object *obj;
- obj = parse_object_buffer(sha1, type, size, buffer, eaten);
+ obj = parse_object_buffer(oid, type, size, buffer, eaten);
if (!obj) {
errors_found |= ERROR_OBJECT;
- return error("%s: object corrupt or missing", sha1_to_hex(sha1));
+ return error("%s: object corrupt or missing", oid_to_hex(oid));
}
obj->flags = HAS_OBJ;
return fsck_obj(obj);
@@ -397,7 +397,7 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
static int default_refs;
static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
- unsigned long timestamp)
+ timestamp_t timestamp)
{
struct object *obj;
@@ -407,7 +407,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
if (timestamp && name_objects)
add_decoration(fsck_walk_options.object_names,
obj,
- xstrfmt("%s@{%ld}", refname, timestamp));
+ xstrfmt("%s@{%"PRItime"}", refname, timestamp));
obj->used = 1;
mark_object_reachable(obj);
} else {
@@ -418,7 +418,7 @@ static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
}
static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
const char *refname = cb_data;
@@ -444,7 +444,7 @@ static int fsck_handle_ref(const char *refname, const struct object_id *oid,
{
struct object *obj;
- obj = parse_object(oid->hash);
+ obj = parse_object(oid);
if (!obj) {
error("%s: invalid sha1 pointer %s", refname, oid_to_hex(oid));
errors_found |= ERROR_REACHABLE;
@@ -506,7 +506,7 @@ static struct object *parse_loose_object(const struct object_id *oid,
if (!contents && type != OBJ_BLOB)
die("BUG: read_loose_object streamed a non-blob");
- obj = parse_object_buffer(oid->hash, type, size, contents, &eaten);
+ obj = parse_object_buffer(oid, type, size, contents, &eaten);
if (!eaten)
free(contents);
@@ -599,10 +599,10 @@ static int fsck_cache_tree(struct cache_tree *it)
fprintf(stderr, "Checking cache tree\n");
if (0 <= it->entry_count) {
- struct object *obj = parse_object(it->sha1);
+ struct object *obj = parse_object(&it->oid);
if (!obj) {
error("%s: invalid sha1 pointer in cache-tree",
- sha1_to_hex(it->sha1));
+ oid_to_hex(&it->oid));
errors_found |= ERROR_REFS;
return 1;
}
@@ -781,7 +781,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
mode = active_cache[i]->ce_mode;
if (S_ISGITLINK(mode))
continue;
- blob = lookup_blob(active_cache[i]->oid.hash);
+ blob = lookup_blob(&active_cache[i]->oid);
if (!blob)
continue;
obj = &blob->object;
diff --git a/builtin/gc.c b/builtin/gc.c
index 91f7696a85..f484eda43c 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,7 +33,7 @@ static int aggressive_window = 250;
static int gc_auto_threshold = 6700;
static int gc_auto_pack_limit = 50;
static int detach_auto = 1;
-static unsigned long gc_log_expire_time;
+static timestamp_t gc_log_expire_time;
static const char *gc_log_expire = "1.day.ago";
static const char *prune_expire = "2.weeks.ago";
static const char *prune_worktrees_expire = "3.months.ago";
diff --git a/builtin/grep.c b/builtin/grep.c
index 3ffb5b4e81..7df9c253ee 100644
--- a/builtin/grep.c
+++ b/builtin/grep.c
@@ -73,14 +73,14 @@ static pthread_mutex_t grep_mutex;
static inline void grep_lock(void)
{
- if (num_threads)
- pthread_mutex_lock(&grep_mutex);
+ assert(num_threads);
+ pthread_mutex_lock(&grep_mutex);
}
static inline void grep_unlock(void)
{
- if (num_threads)
- pthread_mutex_unlock(&grep_mutex);
+ assert(num_threads);
+ pthread_mutex_unlock(&grep_mutex);
}
/* Signalled when a new work_item is added to todo. */
@@ -289,6 +289,17 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)
if (num_threads < 0)
die(_("invalid number of threads specified (%d) for %s"),
num_threads, var);
+#ifdef NO_PTHREADS
+ else if (num_threads && num_threads != 1) {
+ /*
+ * TRANSLATORS: %s is the configuration
+ * variable for tweaking threads, currently
+ * grep.threads
+ */
+ warning(_("no threads support, ignoring %s"), var);
+ num_threads = 0;
+ }
+#endif
}
return st;
@@ -495,6 +506,8 @@ static void compile_submodule_options(const struct grep_opt *opt,
break;
case GREP_PATTERN_TYPE_UNSPECIFIED:
break;
+ default:
+ die("BUG: Added a new grep pattern type without updating switch statement");
}
for (pattern = opt->pattern_list; pattern != NULL;
@@ -866,7 +879,7 @@ static int grep_directory(struct grep_opt *opt, const struct pathspec *pathspec,
if (exc_std)
setup_standard_excludes(&dir);
- fill_directory(&dir, pathspec);
+ fill_directory(&dir, &the_index, pathspec);
for (i = 0; i < dir.nr; i++) {
if (!dir_path_match(dir.entries[i], pathspec, 0, NULL))
continue;
@@ -1190,16 +1203,18 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
break;
}
- if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
+ if (get_sha1_with_context(arg, GET_SHA1_RECORD_PATH,
+ oid.hash, &oc)) {
if (seen_dashdash)
die(_("unable to resolve revision: %s"), arg);
break;
}
- object = parse_object_or_die(oid.hash, arg);
+ object = parse_object_or_die(&oid, arg);
if (!seen_dashdash)
verify_non_filename(prefix, arg);
add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
+ free(oc.path);
}
/*
@@ -1227,6 +1242,8 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
else if (num_threads < 0)
die(_("invalid number of threads specified (%d)"), num_threads);
#else
+ if (num_threads)
+ warning(_("no threads support, ignoring --threads"));
num_threads = 0;
#endif
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 4ff567db47..04b9dcaf0f 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -747,13 +747,13 @@ static int compare_objects(const unsigned char *buf, unsigned long size,
ssize_t len = read_istream(data->st, data->buf, size);
if (len == 0)
die(_("SHA1 COLLISION FOUND WITH %s !"),
- sha1_to_hex(data->entry->idx.sha1));
+ oid_to_hex(&data->entry->idx.oid));
if (len < 0)
die(_("unable to read %s"),
- sha1_to_hex(data->entry->idx.sha1));
+ oid_to_hex(&data->entry->idx.oid));
if (memcmp(buf, data->buf, len))
die(_("SHA1 COLLISION FOUND WITH %s !"),
- sha1_to_hex(data->entry->idx.sha1));
+ oid_to_hex(&data->entry->idx.oid));
size -= len;
buf += len;
}
@@ -771,12 +771,12 @@ static int check_collison(struct object_entry *entry)
memset(&data, 0, sizeof(data));
data.entry = entry;
- data.st = open_istream(entry->idx.sha1, &type, &size, NULL);
+ data.st = open_istream(entry->idx.oid.hash, &type, &size, NULL);
if (!data.st)
return -1;
if (size != entry->size || type != entry->type)
die(_("SHA1 COLLISION FOUND WITH %s !"),
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
unpack_data(entry, compare_objects, &data);
close_istream(data.st);
free(data.buf);
@@ -785,7 +785,7 @@ static int check_collison(struct object_entry *entry)
static void sha1_object(const void *data, struct object_entry *obj_entry,
unsigned long size, enum object_type type,
- const unsigned char *sha1)
+ const struct object_id *oid)
{
void *new_data = NULL;
int collision_test_needed = 0;
@@ -794,7 +794,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
if (startup_info->have_repository) {
read_lock();
- collision_test_needed = has_sha1_file_with_flags(sha1, HAS_SHA1_QUICK);
+ collision_test_needed = has_sha1_file_with_flags(oid->hash, HAS_SHA1_QUICK);
read_unlock();
}
@@ -809,31 +809,31 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
enum object_type has_type;
unsigned long has_size;
read_lock();
- has_type = sha1_object_info(sha1, &has_size);
+ has_type = sha1_object_info(oid->hash, &has_size);
if (has_type < 0)
- die(_("cannot read existing object info %s"), sha1_to_hex(sha1));
+ die(_("cannot read existing object info %s"), oid_to_hex(oid));
if (has_type != type || has_size != size)
- die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
- has_data = read_sha1_file(sha1, &has_type, &has_size);
+ die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
+ has_data = read_sha1_file(oid->hash, &has_type, &has_size);
read_unlock();
if (!data)
data = new_data = get_data_from_pack(obj_entry);
if (!has_data)
- die(_("cannot read existing object %s"), sha1_to_hex(sha1));
+ die(_("cannot read existing object %s"), oid_to_hex(oid));
if (size != has_size || type != has_type ||
memcmp(data, has_data, size) != 0)
- die(_("SHA1 COLLISION FOUND WITH %s !"), sha1_to_hex(sha1));
+ die(_("SHA1 COLLISION FOUND WITH %s !"), oid_to_hex(oid));
free(has_data);
}
if (strict) {
read_lock();
if (type == OBJ_BLOB) {
- struct blob *blob = lookup_blob(sha1);
+ struct blob *blob = lookup_blob(oid);
if (blob)
blob->object.flags |= FLAG_CHECKED;
else
- die(_("invalid blob object %s"), sha1_to_hex(sha1));
+ die(_("invalid blob object %s"), oid_to_hex(oid));
} else {
struct object *obj;
int eaten;
@@ -845,7 +845,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
* we do not need to free the memory here, as the
* buf is deleted by the caller.
*/
- obj = parse_object_buffer(sha1, type, size, buf, &eaten);
+ obj = parse_object_buffer(oid, type, size, buf,
+ &eaten);
if (!obj)
die(_("invalid %s"), typename(type));
if (do_fsck_object &&
@@ -957,9 +958,10 @@ static void resolve_delta(struct object_entry *delta_obj,
if (!result->data)
bad_object(delta_obj->idx.offset, _("failed to apply delta"));
hash_sha1_file(result->data, result->size,
- typename(delta_obj->real_type), delta_obj->idx.sha1);
+ typename(delta_obj->real_type),
+ delta_obj->idx.oid.hash);
sha1_object(result->data, NULL, result->size, delta_obj->real_type,
- delta_obj->idx.sha1);
+ &delta_obj->idx.oid);
counter_lock();
nr_resolved_deltas++;
counter_unlock();
@@ -989,7 +991,7 @@ static struct base_data *find_unresolved_deltas_1(struct base_data *base,
struct base_data *prev_base)
{
if (base->ref_last == -1 && base->ofs_last == -1) {
- find_ref_delta_children(base->obj->idx.sha1,
+ find_ref_delta_children(base->obj->idx.oid.hash,
&base->ref_first, &base->ref_last,
OBJ_REF_DELTA);
@@ -1130,7 +1132,8 @@ static void parse_pack_objects(unsigned char *sha1)
for (i = 0; i < nr_objects; i++) {
struct object_entry *obj = &objects[i];
void *data = unpack_raw_entry(obj, &ofs_delta->offset,
- ref_delta_sha1, obj->idx.sha1);
+ ref_delta_sha1,
+ obj->idx.oid.hash);
obj->real_type = obj->type;
if (obj->type == OBJ_OFS_DELTA) {
nr_ofs_deltas++;
@@ -1146,7 +1149,8 @@ static void parse_pack_objects(unsigned char *sha1)
obj->real_type = OBJ_BAD;
nr_delays++;
} else
- sha1_object(data, NULL, obj->size, obj->type, obj->idx.sha1);
+ sha1_object(data, NULL, obj->size, obj->type,
+ &obj->idx.oid);
free(data);
display_progress(progress, i+1);
}
@@ -1172,7 +1176,8 @@ static void parse_pack_objects(unsigned char *sha1)
if (obj->real_type != OBJ_BAD)
continue;
obj->real_type = obj->type;
- sha1_object(NULL, obj, obj->size, obj->type, obj->idx.sha1);
+ sha1_object(NULL, obj, obj->size, obj->type,
+ &obj->idx.oid);
nr_delays--;
}
if (nr_delays)
@@ -1330,7 +1335,7 @@ static struct object_entry *append_obj_to_pack(struct sha1file *f,
obj[1].idx.offset += write_compressed(f, buf, size);
obj[0].idx.crc32 = crc32_end(f);
sha1flush(f);
- hashcpy(obj->idx.sha1, sha1);
+ hashcpy(obj->idx.oid.hash, sha1);
return obj;
}
@@ -1581,13 +1586,14 @@ static void show_pack_info(int stat_only)
if (stat_only)
continue;
printf("%s %-6s %lu %lu %"PRIuMAX,
- sha1_to_hex(obj->idx.sha1),
+ oid_to_hex(&obj->idx.oid),
typename(obj->real_type), obj->size,
(unsigned long)(obj[1].idx.offset - obj->idx.offset),
(uintmax_t)obj->idx.offset);
if (is_delta_type(obj->type)) {
struct object_entry *bobj = &objects[obj_stat[i].base_object_no];
- printf(" %u %s", obj_stat[i].delta_depth, sha1_to_hex(bobj->idx.sha1));
+ printf(" %u %s", obj_stat[i].delta_depth,
+ oid_to_hex(&bobj->idx.oid));
}
putchar('\n');
}
diff --git a/builtin/log.c b/builtin/log.c
index ec3258368c..e89ec941ce 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -483,16 +483,20 @@ static int show_blob_object(const struct object_id *oid, struct rev_info *rev, c
!DIFF_OPT_TST(&rev->diffopt, ALLOW_TEXTCONV))
return stream_blob_to_fd(1, oid, NULL, 0);
- if (get_sha1_with_context(obj_name, 0, oidc.hash, &obj_context))
+ if (get_sha1_with_context(obj_name, GET_SHA1_RECORD_PATH,
+ oidc.hash, &obj_context))
die(_("Not a valid object name %s"), obj_name);
- if (!obj_context.path[0] ||
- !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size))
+ if (!obj_context.path ||
+ !textconv_object(obj_context.path, obj_context.mode, &oidc, 1, &buf, &size)) {
+ free(obj_context.path);
return stream_blob_to_fd(1, oid, NULL, 0);
+ }
if (!buf)
die(_("git show %s: bad file"), obj_name);
write_or_die(1, buf, size);
+ free(obj_context.path);
return 0;
}
@@ -596,7 +600,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
rev.shown_one = 1;
if (ret)
break;
- o = parse_object(t->tagged->oid.hash);
+ o = parse_object(&t->tagged->oid);
if (!o)
ret = error(_("Could not read object %s"),
oid_to_hex(&t->tagged->oid));
@@ -878,8 +882,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
o2 = rev->pending.objects[1].item;
flags1 = o1->flags;
flags2 = o2->flags;
- c1 = lookup_commit_reference(o1->oid.hash);
- c2 = lookup_commit_reference(o2->oid.hash);
+ c1 = lookup_commit_reference(&o1->oid);
+ c2 = lookup_commit_reference(&o2->oid);
if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
die(_("Not a range."));
@@ -910,8 +914,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
static void gen_message_id(struct rev_info *info, char *base)
{
struct strbuf buf = STRBUF_INIT;
- strbuf_addf(&buf, "%s.%lu.git.%s", base,
- (unsigned long) time(NULL),
+ strbuf_addf(&buf, "%s.%"PRItime".git.%s", base,
+ (timestamp_t) time(NULL),
git_committer_info(IDENT_NO_NAME|IDENT_NO_DATE|IDENT_STRICT));
info->message_id = strbuf_detach(&buf, NULL);
}
@@ -1263,7 +1267,7 @@ static struct commit *get_base_commit(const char *base_commit,
if (get_oid(upstream, &oid))
die(_("Failed to resolve '%s' as a valid ref."), upstream);
- commit = lookup_commit_or_die(oid.hash, "upstream base");
+ commit = lookup_commit_or_die(&oid, "upstream base");
base_list = get_merge_bases_many(commit, total, list);
/* There should be one and only one merge base. */
if (!base_list || base_list->next)
@@ -1819,7 +1823,7 @@ static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
{
struct object_id oid;
if (get_oid(arg, &oid) == 0) {
- struct commit *commit = lookup_commit_reference(oid.hash);
+ struct commit *commit = lookup_commit_reference(&oid);
if (commit) {
commit->object.flags |= flags;
add_pending_object(revs, &commit->object, arg);
diff --git a/builtin/ls-files.c b/builtin/ls-files.c
index a6c70dbe9e..b376afc312 100644
--- a/builtin/ls-files.c
+++ b/builtin/ls-files.c
@@ -97,7 +97,7 @@ static void show_dir_entry(const char *tag, struct dir_entry *ent)
{
int len = max_prefix_len;
- if (len >= ent->len)
+ if (len > ent->len)
die("git ls-files: internal error - directory entry not superset of prefix");
if (!dir_path_match(ent, &pathspec, len, ps_matched))
@@ -238,7 +238,7 @@ static void show_ce_entry(const char *tag, const struct cache_entry *ce)
strbuf_addstr(&name, super_prefix);
strbuf_addstr(&name, ce->name);
- if (len >= ce_namelen(ce))
+ if (len > ce_namelen(ce))
die("git ls-files: internal error - cache entry not superset of prefix");
if (recurse_submodules && S_ISGITLINK(ce->ce_mode) &&
@@ -322,7 +322,7 @@ static void show_ru_info(void)
static int ce_excluded(struct dir_struct *dir, const struct cache_entry *ce)
{
int dtype = ce_to_dtype(ce);
- return is_excluded(dir, ce->name, &dtype);
+ return is_excluded(dir, &the_index, ce->name, &dtype);
}
static void show_files(struct dir_struct *dir)
@@ -333,7 +333,7 @@ static void show_files(struct dir_struct *dir)
if (show_others || show_killed) {
if (!show_others)
dir->flags |= DIR_COLLECT_KILLED_ONLY;
- fill_directory(dir, &pathspec);
+ fill_directory(dir, &the_index, &pathspec);
if (show_others)
show_other_files(dir);
if (show_killed)
@@ -403,6 +403,25 @@ static void prune_cache(const char *prefix, size_t prefixlen)
active_nr = last - pos;
}
+static int get_common_prefix_len(const char *common_prefix)
+{
+ int common_prefix_len;
+
+ if (!common_prefix)
+ return 0;
+
+ common_prefix_len = strlen(common_prefix);
+
+ /*
+ * If the prefix has a trailing slash, strip it so that submodules wont
+ * be pruned from the index.
+ */
+ if (common_prefix[common_prefix_len - 1] == '/')
+ common_prefix_len--;
+
+ return common_prefix_len;
+}
+
/*
* Read the tree specified with --with-tree option
* (typically, HEAD) into stage #1 and then
@@ -414,14 +433,14 @@ static void prune_cache(const char *prefix, size_t prefixlen)
void overlay_tree_on_cache(const char *tree_name, const char *prefix)
{
struct tree *tree;
- unsigned char sha1[20];
+ struct object_id oid;
struct pathspec pathspec;
struct cache_entry *last_stage0 = NULL;
int i;
- if (get_sha1(tree_name, sha1))
+ if (get_oid(tree_name, &oid))
die("tree-ish %s not found.", tree_name);
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(&oid);
if (!tree)
die("bad tree-ish %s", tree_name);
@@ -624,8 +643,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
"--error-unmatch");
parse_pathspec(&pathspec, 0,
- PATHSPEC_PREFER_CWD |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ PATHSPEC_PREFER_CWD,
prefix, argv);
/*
@@ -637,7 +655,9 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
max_prefix = NULL;
else
max_prefix = common_prefix(&pathspec);
- max_prefix_len = max_prefix ? strlen(max_prefix) : 0;
+ max_prefix_len = get_common_prefix_len(max_prefix);
+
+ prune_cache(max_prefix, max_prefix_len);
/* Treat unmatching pathspec elements as errors */
if (pathspec.nr && error_unmatch)
@@ -651,7 +671,6 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
show_killed || show_modified || show_resolve_undo))
show_cached = 1;
- prune_cache(max_prefix, max_prefix_len);
if (with_tree) {
/*
* Basic sanity check; show-stages and show-unmerged
diff --git a/builtin/ls-tree.c b/builtin/ls-tree.c
index d7ebeb4ce6..ee7b293b11 100644
--- a/builtin/ls-tree.c
+++ b/builtin/ls-tree.c
@@ -119,7 +119,7 @@ static int show_tree(const unsigned char *sha1, struct strbuf *base,
int cmd_ls_tree(int argc, const char **argv, const char *prefix)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct tree *tree;
int i, full_tree = 0;
const struct option ls_tree_options[] = {
@@ -164,7 +164,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
if (argc < 1)
usage_with_options(ls_tree_usage, ls_tree_options);
- if (get_sha1(argv[0], sha1))
+ if (get_oid(argv[0], &oid))
die("Not a valid object name %s", argv[0]);
/*
@@ -180,7 +180,7 @@ int cmd_ls_tree(int argc, const char **argv, const char *prefix)
for (i = 0; i < pathspec.nr; i++)
pathspec.items[i].nowildcard_len = pathspec.items[i].len;
pathspec.has_wildcard = 0;
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(&oid);
if (!tree)
die("not a tree object");
return !!read_tree_recursive(tree, "", 0, 0, &pathspec, show_tree, NULL);
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index cfe2a796f8..0c36a70ad8 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -41,7 +41,7 @@ static struct commit *get_commit_reference(const char *arg)
if (get_oid(arg, &revkey))
die("Not a valid object name %s", arg);
- r = lookup_commit_reference(revkey.hash);
+ r = lookup_commit_reference(&revkey);
if (!r)
die("Not a valid commit name %s", arg);
@@ -120,7 +120,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
if (is_null_oid(oid))
return;
- commit = lookup_commit(oid->hash);
+ commit = lookup_commit(oid);
if (!commit ||
(commit->object.flags & TMP_MARK) ||
parse_commit(commit))
@@ -132,7 +132,7 @@ static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
}
static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *ident, unsigned long timestamp,
+ const char *ident, timestamp_t timestamp,
int tz, const char *message, void *cbdata)
{
struct rev_collect *revs = cbdata;
@@ -168,7 +168,7 @@ static int handle_fork_point(int argc, const char **argv)
if (get_oid(commitname, &oid))
die("Not a valid object name: '%s'", commitname);
- derived = lookup_commit_reference(oid.hash);
+ derived = lookup_commit_reference(&oid);
memset(&revs, 0, sizeof(revs));
revs.initial = 1;
for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
diff --git a/builtin/merge-tree.c b/builtin/merge-tree.c
index 5b7ab9b967..bad6735c76 100644
--- a/builtin/merge-tree.c
+++ b/builtin/merge-tree.c
@@ -161,14 +161,14 @@ static int both_empty(struct name_entry *a, struct name_entry *b)
return !(a->oid || b->oid);
}
-static struct merge_list *create_entry(unsigned stage, unsigned mode, const unsigned char *sha1, const char *path)
+static struct merge_list *create_entry(unsigned stage, unsigned mode, const struct object_id *oid, const char *path)
{
struct merge_list *res = xcalloc(1, sizeof(*res));
res->stage = stage;
res->path = path;
res->mode = mode;
- res->blob = lookup_blob(sha1);
+ res->blob = lookup_blob(oid);
return res;
}
@@ -188,8 +188,8 @@ static void resolve(const struct traverse_info *info, struct name_entry *ours, s
return;
path = traverse_path(info, result);
- orig = create_entry(2, ours->mode, ours->oid->hash, path);
- final = create_entry(0, result->mode, result->oid->hash, path);
+ orig = create_entry(2, ours->mode, ours->oid, path);
+ final = create_entry(0, result->mode, result->oid, path);
final->link = orig;
@@ -239,7 +239,7 @@ static struct merge_list *link_entry(unsigned stage, const struct traverse_info
path = entry->path;
else
path = traverse_path(info, n);
- link = create_entry(stage, n->mode, n->oid->hash, path);
+ link = create_entry(stage, n->mode, n->oid, path);
link->link = entry;
return link;
}
diff --git a/builtin/merge.c b/builtin/merge.c
index 703827f006..a4a098f40f 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -605,13 +605,13 @@ static int read_tree_trivial(struct object_id *common, struct object_id *head,
opts.verbose_update = 1;
opts.trivial_merges_only = 1;
opts.merge = 1;
- trees[nr_trees] = parse_tree_indirect(common->hash);
+ trees[nr_trees] = parse_tree_indirect(common);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(head->hash);
+ trees[nr_trees] = parse_tree_indirect(head);
if (!trees[nr_trees++])
return -1;
- trees[nr_trees] = parse_tree_indirect(one->hash);
+ trees[nr_trees] = parse_tree_indirect(one);
if (!trees[nr_trees++])
return -1;
opts.fn = threeway_merge;
@@ -1123,7 +1123,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
if (!branch || is_null_oid(&head_oid))
head_commit = NULL;
else
- head_commit = lookup_commit_or_die(head_oid.hash, "HEAD");
+ head_commit = lookup_commit_or_die(&head_oid, "HEAD");
init_diff_ui_defaults();
git_config(git_merge_config, NULL);
@@ -1372,8 +1372,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
goto done;
}
- if (checkout_fast_forward(head_commit->object.oid.hash,
- commit->object.oid.hash,
+ if (checkout_fast_forward(&head_commit->object.oid,
+ &commit->object.oid,
overwrite_ignore)) {
ret = 1;
goto done;
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index e7a3fe7ee7..7fc7e66e85 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -10,19 +10,58 @@
typedef struct rev_name {
const char *tip_name;
- unsigned long taggerdate;
+ timestamp_t taggerdate;
int generation;
int distance;
+ int from_tag;
} rev_name;
-static long cutoff = LONG_MAX;
+static timestamp_t cutoff = TIME_MAX;
/* How many generations are maximally preferred over _one_ merge traversal? */
#define MERGE_TRAVERSAL_WEIGHT 65535
+static int is_better_name(struct rev_name *name,
+ const char *tip_name,
+ timestamp_t taggerdate,
+ int generation,
+ int distance,
+ int from_tag)
+{
+ /*
+ * When comparing names based on tags, prefer names
+ * based on the older tag, even if it is farther away.
+ */
+ if (from_tag && name->from_tag)
+ return (name->taggerdate > taggerdate ||
+ (name->taggerdate == taggerdate &&
+ name->distance > distance));
+
+ /*
+ * We know that at least one of them is a non-tag at this point.
+ * favor a tag over a non-tag.
+ */
+ if (name->from_tag != from_tag)
+ return from_tag;
+
+ /*
+ * We are now looking at two non-tags. Tiebreak to favor
+ * shorter hops.
+ */
+ if (name->distance != distance)
+ return name->distance > distance;
+
+ /* ... or tiebreak to favor older date */
+ if (name->taggerdate != taggerdate)
+ return name->taggerdate > taggerdate;
+
+ /* keep the current one if we cannot decide */
+ return 0;
+}
+
static void name_rev(struct commit *commit,
- const char *tip_name, unsigned long taggerdate,
- int generation, int distance,
+ const char *tip_name, timestamp_t taggerdate,
+ int generation, int distance, int from_tag,
int deref)
{
struct rev_name *name = (struct rev_name *)commit->util;
@@ -46,14 +85,14 @@ static void name_rev(struct commit *commit,
name = xmalloc(sizeof(rev_name));
commit->util = name;
goto copy_data;
- } else if (name->taggerdate > taggerdate ||
- (name->taggerdate == taggerdate &&
- name->distance > distance)) {
+ } else if (is_better_name(name, tip_name, taggerdate,
+ generation, distance, from_tag)) {
copy_data:
name->tip_name = tip_name;
name->taggerdate = taggerdate;
name->generation = generation;
name->distance = distance;
+ name->from_tag = from_tag;
} else {
free(to_free);
return;
@@ -75,10 +114,12 @@ copy_data:
parent_number);
name_rev(parents->item, new_name, taggerdate, 0,
- distance + MERGE_TRAVERSAL_WEIGHT, 0);
+ distance + MERGE_TRAVERSAL_WEIGHT,
+ from_tag, 0);
} else {
name_rev(parents->item, tip_name, taggerdate,
- generation + 1, distance + 1, 0);
+ generation + 1, distance + 1,
+ from_tag, 0);
}
}
}
@@ -117,7 +158,7 @@ struct name_ref_data {
static struct tip_table {
struct tip_table_entry {
- unsigned char sha1[20];
+ struct object_id oid;
const char *refname;
} *table;
int nr;
@@ -125,13 +166,13 @@ static struct tip_table {
int sorted;
} tip_table;
-static void add_to_tip_table(const unsigned char *sha1, const char *refname,
+static void add_to_tip_table(const struct object_id *oid, const char *refname,
int shorten_unambiguous)
{
refname = name_ref_abbrev(refname, shorten_unambiguous);
ALLOC_GROW(tip_table.table, tip_table.nr + 1, tip_table.alloc);
- hashcpy(tip_table.table[tip_table.nr].sha1, sha1);
+ oidcpy(&tip_table.table[tip_table.nr].oid, oid);
tip_table.table[tip_table.nr].refname = xstrdup(refname);
tip_table.nr++;
tip_table.sorted = 0;
@@ -140,16 +181,16 @@ static void add_to_tip_table(const unsigned char *sha1, const char *refname,
static int tipcmp(const void *a_, const void *b_)
{
const struct tip_table_entry *a = a_, *b = b_;
- return hashcmp(a->sha1, b->sha1);
+ return oidcmp(&a->oid, &b->oid);
}
static int name_ref(const char *path, const struct object_id *oid, int flags, void *cb_data)
{
- struct object *o = parse_object(oid->hash);
+ struct object *o = parse_object(oid);
struct name_ref_data *data = cb_data;
int can_abbreviate_output = data->tags_only && data->name_only;
int deref = 0;
- unsigned long taggerdate = ULONG_MAX;
+ timestamp_t taggerdate = TIME_MAX;
if (data->tags_only && !starts_with(path, "refs/tags/"))
return 0;
@@ -197,21 +238,25 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
return 0;
}
- add_to_tip_table(oid->hash, path, can_abbreviate_output);
+ add_to_tip_table(oid, path, can_abbreviate_output);
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
if (!t->tagged)
break; /* broken repository */
- o = parse_object(t->tagged->oid.hash);
+ o = parse_object(&t->tagged->oid);
deref = 1;
taggerdate = t->date;
}
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
+ int from_tag = starts_with(path, "refs/tags/");
+ if (taggerdate == ULONG_MAX)
+ taggerdate = ((struct commit *)o)->date;
path = name_ref_abbrev(path, can_abbreviate_output);
- name_rev(commit, xstrdup(path), taggerdate, 0, 0, deref);
+ name_rev(commit, xstrdup(path), taggerdate, 0, 0,
+ from_tag, deref);
}
return 0;
}
@@ -219,7 +264,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
static const unsigned char *nth_tip_table_ent(size_t ix, void *table_)
{
struct tip_table_entry *table = table_;
- return table[ix].sha1;
+ return table[ix].oid.hash;
}
static const char *get_exact_ref_match(const struct object *o)
@@ -304,9 +349,9 @@ static void name_rev_line(char *p, struct name_ref_data *data)
#define ishex(x) (isdigit((x)) || ((x) >= 'a' && (x) <= 'f'))
if (!ishex(*p))
forty = 0;
- else if (++forty == 40 &&
+ else if (++forty == GIT_SHA1_HEXSZ &&
!ishex(*(p+1))) {
- unsigned char sha1[40];
+ struct object_id oid;
const char *name = NULL;
char c = *(p+1);
int p_len = p - p_start + 1;
@@ -314,9 +359,9 @@ static void name_rev_line(char *p, struct name_ref_data *data)
forty = 0;
*(p+1) = 0;
- if (!get_sha1(p - 39, sha1)) {
+ if (!get_oid(p - (GIT_SHA1_HEXSZ - 1), &oid)) {
struct object *o =
- lookup_object(sha1);
+ lookup_object(oid.hash);
if (o)
name = get_rev_name(o, &buf);
}
@@ -326,7 +371,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
continue;
if (data->name_only)
- printf("%.*s%s", p_len - 40, p_start, name);
+ printf("%.*s%s", p_len - GIT_SHA1_HEXSZ, p_start, name);
else
printf("%.*s (%s)", p_len, p_start, name);
p_start = p + 1;
@@ -377,18 +422,18 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
cutoff = 0;
for (; argc; argc--, argv++) {
- unsigned char sha1[20];
+ struct object_id oid;
struct object *object;
struct commit *commit;
- if (get_sha1(*argv, sha1)) {
+ if (get_oid(*argv, &oid)) {
fprintf(stderr, "Could not get sha1 for %s. Skipping.\n",
*argv);
continue;
}
commit = NULL;
- object = parse_object(sha1);
+ object = parse_object(&oid);
if (object) {
struct object *peeled = deref_tag(object, *argv, 0);
if (peeled && peeled->type == OBJ_COMMIT)
diff --git a/builtin/notes.c b/builtin/notes.c
index fb856e53b6..7196bff0eb 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -708,7 +708,7 @@ static int merge_commit(struct notes_merge_options *o)
if (get_oid("NOTES_MERGE_PARTIAL", &oid))
die(_("failed to read ref NOTES_MERGE_PARTIAL"));
- else if (!(partial = lookup_commit_reference(oid.hash)))
+ else if (!(partial = lookup_commit_reference(&oid)))
die(_("could not find commit from NOTES_MERGE_PARTIAL."));
else if (parse_commit(partial))
die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 50e01aa80e..f672225def 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -44,7 +44,7 @@ static uint32_t nr_result, nr_written;
static int non_empty;
static int reuse_delta = 1, reuse_object = 1;
static int keep_unreachable, unpack_unreachable, include_tag;
-static unsigned long unpack_unreachable_expiration;
+static timestamp_t unpack_unreachable_expiration;
static int pack_loose_unreachable;
static int local;
static int have_non_local_packs;
@@ -106,12 +106,14 @@ static void *get_delta(struct object_entry *entry)
void *buf, *base_buf, *delta_buf;
enum object_type type;
- buf = read_sha1_file(entry->idx.sha1, &type, &size);
+ buf = read_sha1_file(entry->idx.oid.hash, &type, &size);
if (!buf)
- die("unable to read %s", sha1_to_hex(entry->idx.sha1));
- base_buf = read_sha1_file(entry->delta->idx.sha1, &type, &base_size);
+ die("unable to read %s", oid_to_hex(&entry->idx.oid));
+ base_buf = read_sha1_file(entry->delta->idx.oid.hash, &type,
+ &base_size);
if (!base_buf)
- die("unable to read %s", sha1_to_hex(entry->delta->idx.sha1));
+ die("unable to read %s",
+ oid_to_hex(&entry->delta->idx.oid));
delta_buf = diff_delta(base_buf, base_size,
buf, size, &delta_size, 0);
if (!delta_buf || delta_size != entry->delta_size)
@@ -249,12 +251,14 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
if (!usable_delta) {
if (entry->type == OBJ_BLOB &&
entry->size > big_file_threshold &&
- (st = open_istream(entry->idx.sha1, &type, &size, NULL)) != NULL)
+ (st = open_istream(entry->idx.oid.hash, &type, &size, NULL)) != NULL)
buf = NULL;
else {
- buf = read_sha1_file(entry->idx.sha1, &type, &size);
+ buf = read_sha1_file(entry->idx.oid.hash, &type,
+ &size);
if (!buf)
- die(_("unable to read %s"), sha1_to_hex(entry->idx.sha1));
+ die(_("unable to read %s"),
+ oid_to_hex(&entry->idx.oid));
}
/*
* make sure no cached delta data remains from a
@@ -322,7 +326,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
return 0;
}
sha1write(f, header, hdrlen);
- sha1write(f, entry->delta->idx.sha1, 20);
+ sha1write(f, entry->delta->idx.oid.hash, 20);
hdrlen += 20;
} else {
if (limit && hdrlen + datalen + 20 >= limit) {
@@ -334,7 +338,7 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
sha1write(f, header, hdrlen);
}
if (st) {
- datalen = write_large_blob_data(st, f, entry->idx.sha1);
+ datalen = write_large_blob_data(st, f, entry->idx.oid.hash);
close_istream(st);
} else {
sha1write(f, buf, datalen);
@@ -369,7 +373,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
datalen = revidx[1].offset - offset;
if (!pack_to_stdout && p->index_version > 1 &&
check_pack_crc(p, &w_curs, offset, datalen, revidx->nr)) {
- error("bad packed object CRC for %s", sha1_to_hex(entry->idx.sha1));
+ error("bad packed object CRC for %s",
+ oid_to_hex(&entry->idx.oid));
unuse_pack(&w_curs);
return write_no_reuse_object(f, entry, limit, usable_delta);
}
@@ -379,7 +384,8 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
if (!pack_to_stdout && p->index_version == 1 &&
check_pack_inflate(p, &w_curs, offset, datalen, entry->size)) {
- error("corrupt packed object for %s", sha1_to_hex(entry->idx.sha1));
+ error("corrupt packed object for %s",
+ oid_to_hex(&entry->idx.oid));
unuse_pack(&w_curs);
return write_no_reuse_object(f, entry, limit, usable_delta);
}
@@ -404,7 +410,7 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
return 0;
}
sha1write(f, header, hdrlen);
- sha1write(f, entry->delta->idx.sha1, 20);
+ sha1write(f, entry->delta->idx.oid.hash, 20);
hdrlen += 20;
reused_delta++;
} else {
@@ -509,7 +515,7 @@ static enum write_one_status write_one(struct sha1file *f,
recursing = (e->idx.offset == 1);
if (recursing) {
warning("recursive delta detected for object %s",
- sha1_to_hex(e->idx.sha1));
+ oid_to_hex(&e->idx.oid));
return WRITE_ONE_RECURSIVE;
} else if (e->idx.offset || e->preferred_base) {
/* offset is non zero if object is written already. */
@@ -1432,7 +1438,7 @@ static void check_object(struct object_entry *entry)
ofs += 1;
if (!ofs || MSB(ofs, 7)) {
error("delta base offset overflow in pack for %s",
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
goto give_up;
}
c = buf[used_0++];
@@ -1441,7 +1447,7 @@ static void check_object(struct object_entry *entry)
ofs = entry->in_pack_offset - ofs;
if (ofs <= 0 || ofs >= entry->in_pack_offset) {
error("delta base offset out of bound for %s",
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
goto give_up;
}
if (reuse_delta && !entry->preferred_base) {
@@ -1498,7 +1504,7 @@ static void check_object(struct object_entry *entry)
unuse_pack(&w_curs);
}
- entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
+ entry->type = sha1_object_info(entry->idx.oid.hash, &entry->size);
/*
* The error condition is checked in prepare_pack(). This is
* to permit a missing preferred base object to be ignored
@@ -1514,7 +1520,7 @@ static int pack_offset_sort(const void *_a, const void *_b)
/* avoid filesystem trashing with loose objects */
if (!a->in_pack && !b->in_pack)
- return hashcmp(a->idx.sha1, b->idx.sha1);
+ return oidcmp(&a->idx.oid, &b->idx.oid);
if (a->in_pack < b->in_pack)
return -1;
@@ -1560,7 +1566,8 @@ static void drop_reused_delta(struct object_entry *entry)
* And if that fails, the error will be recorded in entry->type
* and dealt with in prepare_pack().
*/
- entry->type = sha1_object_info(entry->idx.sha1, &entry->size);
+ entry->type = sha1_object_info(entry->idx.oid.hash,
+ &entry->size);
}
}
@@ -1852,26 +1859,29 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
/* Load data if not already done */
if (!trg->data) {
read_lock();
- trg->data = read_sha1_file(trg_entry->idx.sha1, &type, &sz);
+ trg->data = read_sha1_file(trg_entry->idx.oid.hash, &type,
+ &sz);
read_unlock();
if (!trg->data)
die("object %s cannot be read",
- sha1_to_hex(trg_entry->idx.sha1));
+ oid_to_hex(&trg_entry->idx.oid));
if (sz != trg_size)
die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(trg_entry->idx.sha1), sz, trg_size);
+ oid_to_hex(&trg_entry->idx.oid), sz,
+ trg_size);
*mem_usage += sz;
}
if (!src->data) {
read_lock();
- src->data = read_sha1_file(src_entry->idx.sha1, &type, &sz);
+ src->data = read_sha1_file(src_entry->idx.oid.hash, &type,
+ &sz);
read_unlock();
if (!src->data) {
if (src_entry->preferred_base) {
static int warned = 0;
if (!warned++)
warning("object %s cannot be read",
- sha1_to_hex(src_entry->idx.sha1));
+ oid_to_hex(&src_entry->idx.oid));
/*
* Those objects are not included in the
* resulting pack. Be resilient and ignore
@@ -1881,11 +1891,12 @@ static int try_delta(struct unpacked *trg, struct unpacked *src,
return 0;
}
die("object %s cannot be read",
- sha1_to_hex(src_entry->idx.sha1));
+ oid_to_hex(&src_entry->idx.oid));
}
if (sz != src_size)
die("object %s inconsistent object length (%lu vs %lu)",
- sha1_to_hex(src_entry->idx.sha1), sz, src_size);
+ oid_to_hex(&src_entry->idx.oid), sz,
+ src_size);
*mem_usage += sz;
}
if (!src->index) {
@@ -2337,7 +2348,7 @@ static void add_tag_chain(const struct object_id *oid)
if (packlist_find(&to_pack, oid->hash, NULL))
return;
- tag = lookup_tag(oid->hash);
+ tag = lookup_tag(oid);
while (1) {
if (!tag || parse_tag(tag) || !tag->tagged)
die("unable to pack objects reachable from tag %s",
@@ -2406,7 +2417,7 @@ static void prepare_pack(int window, int depth)
nr_deltas++;
if (entry->type < 0)
die("unable to get type of object %s",
- sha1_to_hex(entry->idx.sha1));
+ oid_to_hex(&entry->idx.oid));
} else {
if (entry->type < 0) {
/*
@@ -2472,8 +2483,10 @@ static int git_pack_config(const char *k, const char *v, void *cb)
die("invalid number of threads specified (%d)",
delta_search_threads);
#ifdef NO_PTHREADS
- if (delta_search_threads != 1)
+ if (delta_search_threads != 1) {
warning("no threads support, ignoring %s", k);
+ delta_search_threads = 0;
+ }
#endif
return 0;
}
@@ -2675,7 +2688,7 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
static struct oid_array recent_objects;
static int loosened_object_can_be_discarded(const struct object_id *oid,
- unsigned long mtime)
+ timestamp_t mtime)
{
if (!unpack_unreachable_expiration)
return 0;
@@ -2781,10 +2794,10 @@ static void get_object_list(int ac, const char **av)
continue;
}
if (starts_with(line, "--shallow ")) {
- unsigned char sha1[20];
- if (get_sha1_hex(line + 10, sha1))
+ struct object_id oid;
+ if (get_oid_hex(line + 10, &oid))
die("not an SHA-1 '%s'", line + 10);
- register_shallow(sha1);
+ register_shallow(&oid);
use_bitmap_index = 0;
continue;
}
diff --git a/builtin/prune.c b/builtin/prune.c
index 42633e0c6e..f0e2bff04c 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -13,7 +13,7 @@ static const char * const prune_usage[] = {
};
static int show_only;
static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
static int show_progress = -1;
static int prune_tmp_file(const char *fullpath)
@@ -111,7 +111,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
};
char *s;
- expire = ULONG_MAX;
+ expire = TIME_MAX;
save_commit_buffer = 0;
check_replace_refs = 0;
ref_paranoia = 1;
@@ -123,11 +123,12 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
die(_("cannot prune in a precious-objects repo"));
while (argc--) {
- unsigned char sha1[20];
+ struct object_id oid;
const char *name = *argv++;
- if (!get_sha1(name, sha1)) {
- struct object *object = parse_object_or_die(sha1, name);
+ if (!get_oid(name, &oid)) {
+ struct object *object = parse_object_or_die(&oid,
+ name);
add_pending_object(&revs, object, "");
}
else
diff --git a/builtin/pull.c b/builtin/pull.c
index dd1a4a94e4..da8b60fc85 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -523,7 +523,7 @@ 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(EMPTY_TREE_SHA1_BIN, merge_head->hash, 0))
+ if (checkout_fast_forward(&empty_tree_oid, merge_head, 0))
return 1;
if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR))
@@ -698,10 +698,10 @@ static int get_octopus_merge_base(struct object_id *merge_base,
{
struct commit_list *revs = NULL, *result;
- commit_list_insert(lookup_commit_reference(curr_head->hash), &revs);
- commit_list_insert(lookup_commit_reference(merge_head->hash), &revs);
+ commit_list_insert(lookup_commit_reference(curr_head), &revs);
+ commit_list_insert(lookup_commit_reference(merge_head), &revs);
if (!is_null_oid(fork_point))
- commit_list_insert(lookup_commit_reference(fork_point->hash), &revs);
+ commit_list_insert(lookup_commit_reference(fork_point), &revs);
result = reduce_heads(get_octopus_merge_bases(revs));
free_commit_list(revs);
@@ -772,6 +772,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
struct oid_array merge_heads = OID_ARRAY_INIT;
struct object_id orig_head, curr_head;
struct object_id rebase_fork_point;
+ int autostash;
if (!getenv("GIT_REFLOG_ACTION"))
set_reflog_message(argc, argv);
@@ -800,8 +801,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
if (!opt_rebase && opt_autostash != -1)
die(_("--[no-]autostash option is only valid with --rebase."));
+ autostash = config_autostash;
if (opt_rebase) {
- int autostash = config_autostash;
if (opt_autostash != -1)
autostash = opt_autostash;
@@ -839,7 +840,7 @@ 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.hash, curr_head.hash, 0))
+ if (checkout_fast_forward(&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"
@@ -862,16 +863,18 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
die(_("Cannot rebase onto multiple branches."));
if (opt_rebase) {
- struct commit_list *list = NULL;
- struct commit *merge_head, *head;
-
- head = lookup_commit_reference(orig_head.hash);
- commit_list_insert(head, &list);
- merge_head = lookup_commit_reference(merge_heads.oid[0].hash);
- if (is_descendant_of(merge_head, list)) {
- /* we can fast-forward this without invoking rebase */
- opt_ff = "--ff-only";
- return run_merge();
+ if (!autostash) {
+ struct commit_list *list = NULL;
+ struct commit *merge_head, *head;
+
+ head = lookup_commit_reference(&orig_head);
+ commit_list_insert(head, &list);
+ merge_head = lookup_commit_reference(&merge_heads.oid[0]);
+ if (is_descendant_of(merge_head, list)) {
+ /* we can fast-forward this without invoking rebase */
+ opt_ff = "--ff-only";
+ return run_merge();
+ }
}
return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
} else {
diff --git a/builtin/read-tree.c b/builtin/read-tree.c
index a52a9e11bb..78d3193659 100644
--- a/builtin/read-tree.c
+++ b/builtin/read-tree.c
@@ -23,13 +23,13 @@ static int read_empty;
static struct tree *trees[MAX_UNPACK_TREES];
static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-static int list_tree(unsigned char *sha1)
+static int list_tree(struct object_id *oid)
{
struct tree *tree;
if (nr_trees >= MAX_UNPACK_TREES)
die("I cannot read more than %d trees", MAX_UNPACK_TREES);
- tree = parse_tree_indirect(sha1);
+ tree = parse_tree_indirect(oid);
if (!tree)
return -1;
trees[nr_trees++] = tree;
@@ -121,7 +121,7 @@ static struct lock_file lock_file;
int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
{
int i, stage = 0;
- unsigned char sha1[20];
+ struct object_id oid;
struct tree_desc t[MAX_UNPACK_TREES];
struct unpack_trees_options opts;
int prefix_set = 0;
@@ -204,9 +204,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
for (i = 0; i < argc; i++) {
const char *arg = argv[i];
- if (get_sha1(arg, sha1))
+ if (get_oid(arg, &oid))
die("Not a valid object name %s", arg);
- if (list_tree(sha1) < 0)
+ if (list_tree(&oid) < 0)
die("failed to unpack tree object %s", arg);
stage++;
}
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index da9a3a2c9d..b1706a5731 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -78,7 +78,7 @@ static const char *NONCE_OK = "OK";
static const char *NONCE_SLOP = "SLOP";
static const char *nonce_status;
static long nonce_stamp_slop;
-static unsigned long nonce_stamp_slop_limit;
+static timestamp_t nonce_stamp_slop_limit;
static struct ref_transaction *transaction;
static enum {
@@ -454,17 +454,17 @@ static void hmac_sha1(unsigned char *out,
git_SHA1_Final(out, &ctx);
}
-static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
+static char *prepare_push_cert_nonce(const char *path, timestamp_t stamp)
{
struct strbuf buf = STRBUF_INIT;
unsigned char sha1[20];
- strbuf_addf(&buf, "%s:%lu", path, stamp);
+ strbuf_addf(&buf, "%s:%"PRItime, path, stamp);
hmac_sha1(sha1, buf.buf, buf.len, cert_nonce_seed, strlen(cert_nonce_seed));;
strbuf_release(&buf);
/* RFC 2104 5. HMAC-SHA1-80 */
- strbuf_addf(&buf, "%lu-%.*s", stamp, 20, sha1_to_hex(sha1));
+ strbuf_addf(&buf, "%"PRItime"-%.*s", stamp, 20, sha1_to_hex(sha1));
return strbuf_detach(&buf, NULL);
}
@@ -499,7 +499,7 @@ static char *find_header(const char *msg, size_t len, const char *key,
static const char *check_nonce(const char *buf, size_t len)
{
char *nonce = find_header(buf, len, "nonce", NULL);
- unsigned long stamp, ostamp;
+ timestamp_t stamp, ostamp;
char *bohmac, *expect = NULL;
const char *retval = NONCE_BAD;
@@ -537,7 +537,7 @@ static const char *check_nonce(const char *buf, size_t len)
retval = NONCE_BAD;
goto leave;
}
- stamp = strtoul(nonce, &bohmac, 10);
+ stamp = parse_timestamp(nonce, &bohmac, 10);
if (bohmac == nonce || bohmac[0] != '-') {
retval = NONCE_BAD;
goto leave;
@@ -555,7 +555,7 @@ static const char *check_nonce(const char *buf, size_t len)
* would mean it was issued by another server with its clock
* skewed in the future.
*/
- ostamp = strtoul(push_cert_nonce, NULL, 10);
+ ostamp = parse_timestamp(push_cert_nonce, NULL, 10);
nonce_stamp_slop = (long)ostamp - (long)stamp;
if (nonce_stamp_slop_limit &&
@@ -900,7 +900,7 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
* not lose these new roots..
*/
for (i = 0; i < extra.nr; i++)
- register_shallow(extra.oid[i].hash);
+ register_shallow(&extra.oid[i]);
si->shallow_ref[cmd->index] = 0;
oid_array_clear(&extra);
@@ -1102,8 +1102,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
struct object *old_object, *new_object;
struct commit *old_commit, *new_commit;
- old_object = parse_object(old_oid->hash);
- new_object = parse_object(new_oid->hash);
+ old_object = parse_object(old_oid);
+ new_object = parse_object(new_oid);
if (!old_object || !new_object ||
old_object->type != OBJ_COMMIT ||
@@ -1126,7 +1126,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
if (is_null_oid(new_oid)) {
struct strbuf err = STRBUF_INIT;
- if (!parse_object(old_oid->hash)) {
+ if (!parse_object(old_oid)) {
old_oid = NULL;
if (ref_exists(name)) {
rp_warning("Allowing deletion of corrupt ref.");
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 7472775778..920c16dac0 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -16,14 +16,14 @@ static const char reflog_delete_usage[] =
static const char reflog_exists_usage[] =
"git reflog exists <ref>";
-static unsigned long default_reflog_expire;
-static unsigned long default_reflog_expire_unreachable;
+static timestamp_t default_reflog_expire;
+static timestamp_t default_reflog_expire_unreachable;
struct cmd_reflog_expire_cb {
struct rev_info revs;
int stalefix;
- unsigned long expire_total;
- unsigned long expire_unreachable;
+ timestamp_t expire_total;
+ timestamp_t expire_unreachable;
int recno;
};
@@ -55,14 +55,14 @@ struct collect_reflog_cb {
#define STUDYING (1u<<11)
#define REACHABLE (1u<<12)
-static int tree_is_complete(const unsigned char *sha1)
+static int tree_is_complete(const struct object_id *oid)
{
struct tree_desc desc;
struct name_entry entry;
int complete;
struct tree *tree;
- tree = lookup_tree(sha1);
+ tree = lookup_tree(oid);
if (!tree)
return 0;
if (tree->object.flags & SEEN)
@@ -73,7 +73,7 @@ static int tree_is_complete(const unsigned char *sha1)
if (!tree->buffer) {
enum object_type type;
unsigned long size;
- void *data = read_sha1_file(sha1, &type, &size);
+ void *data = read_sha1_file(oid->hash, &type, &size);
if (!data) {
tree->object.flags |= INCOMPLETE;
return 0;
@@ -85,7 +85,7 @@ static int tree_is_complete(const unsigned char *sha1)
complete = 1;
while (tree_entry(&desc, &entry)) {
if (!has_sha1_file(entry.oid->hash) ||
- (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid->hash))) {
+ (S_ISDIR(entry.mode) && !tree_is_complete(entry.oid))) {
tree->object.flags |= INCOMPLETE;
complete = 0;
}
@@ -126,7 +126,7 @@ static int commit_is_complete(struct commit *commit)
struct commit_list *parent;
c = (struct commit *)study.objects[--study.nr].item;
- if (!c->object.parsed && !parse_object(c->object.oid.hash))
+ if (!c->object.parsed && !parse_object(&c->object.oid))
c->object.flags |= INCOMPLETE;
if (c->object.flags & INCOMPLETE) {
@@ -152,7 +152,7 @@ static int commit_is_complete(struct commit *commit)
for (i = 0; i < found.nr; i++) {
struct commit *c =
(struct commit *)found.objects[i].item;
- if (!tree_is_complete(c->tree->object.oid.hash)) {
+ if (!tree_is_complete(&c->tree->object.oid)) {
is_incomplete = 1;
c->object.flags |= INCOMPLETE;
}
@@ -186,13 +186,13 @@ static int commit_is_complete(struct commit *commit)
return !is_incomplete;
}
-static int keep_entry(struct commit **it, unsigned char *sha1)
+static int keep_entry(struct commit **it, struct object_id *oid)
{
struct commit *commit;
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return 1;
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid, 1);
if (!commit)
return 0;
@@ -219,7 +219,7 @@ static int keep_entry(struct commit **it, unsigned char *sha1)
static void mark_reachable(struct expire_reflog_policy_cb *cb)
{
struct commit_list *pending;
- unsigned long expire_limit = cb->mark_limit;
+ timestamp_t expire_limit = cb->mark_limit;
struct commit_list *leftover = NULL;
for (pending = cb->mark_list; pending; pending = pending->next)
@@ -251,17 +251,17 @@ static void mark_reachable(struct expire_reflog_policy_cb *cb)
cb->mark_list = leftover;
}
-static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, unsigned char *sha1)
+static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit, struct object_id *oid)
{
/*
* We may or may not have the commit yet - if not, look it
* up using the supplied sha1.
*/
if (!commit) {
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return 0;
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(oid, 1);
/* Not a commit -- keep it */
if (!commit)
@@ -283,8 +283,8 @@ static int unreachable(struct expire_reflog_policy_cb *cb, struct commit *commit
/*
* Return true iff the specified reflog entry should be expired.
*/
-static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
- const char *email, unsigned long timestamp, int tz,
+static int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
@@ -295,13 +295,13 @@ static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
old = new = NULL;
if (cb->cmd.stalefix &&
- (!keep_entry(&old, osha1) || !keep_entry(&new, nsha1)))
+ (!keep_entry(&old, ooid) || !keep_entry(&new, noid)))
return 1;
if (timestamp < cb->cmd.expire_unreachable) {
if (cb->unreachable_expire_kind == UE_ALWAYS)
return 1;
- if (unreachable(cb, old, osha1) || unreachable(cb, new, nsha1))
+ if (unreachable(cb, old, ooid) || unreachable(cb, new, noid))
return 1;
}
@@ -318,7 +318,7 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid,
struct commit *tip_commit;
if (flags & REF_ISSYMREF)
return 0;
- tip_commit = lookup_commit_reference_gently(oid->hash, 1);
+ tip_commit = lookup_commit_reference_gently(oid, 1);
if (!tip_commit)
return 0;
commit_list_insert(tip_commit, list);
@@ -326,7 +326,7 @@ static int push_tip_to_list(const char *refname, const struct object_id *oid,
}
static void reflog_expiry_prepare(const char *refname,
- const unsigned char *sha1,
+ const struct object_id *oid,
void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
@@ -335,7 +335,7 @@ static void reflog_expiry_prepare(const char *refname,
cb->tip_commit = NULL;
cb->unreachable_expire_kind = UE_HEAD;
} else {
- cb->tip_commit = lookup_commit_reference_gently(sha1, 1);
+ cb->tip_commit = lookup_commit_reference_gently(oid, 1);
if (!cb->tip_commit)
cb->unreachable_expire_kind = UE_ALWAYS;
else
@@ -392,8 +392,8 @@ static int collect_reflog(const char *ref, const struct object_id *oid, int unus
static struct reflog_expire_cfg {
struct reflog_expire_cfg *next;
- unsigned long expire_total;
- unsigned long expire_unreachable;
+ timestamp_t expire_total;
+ timestamp_t expire_unreachable;
char pattern[FLEX_ARRAY];
} *reflog_expire_cfg, **reflog_expire_cfg_tail;
@@ -415,7 +415,7 @@ static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
return ent;
}
-static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
+static int parse_expire_cfg_value(const char *var, const char *value, timestamp_t *expire)
{
if (!value)
return config_error_nonbool(var);
@@ -433,7 +433,7 @@ static int reflog_expire_config(const char *var, const char *value, void *cb)
{
const char *pattern, *key;
int pattern_len;
- unsigned long expire;
+ timestamp_t expire;
int slot;
struct reflog_expire_cfg *ent;
@@ -515,7 +515,7 @@ static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, c
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
{
struct expire_reflog_policy_cb cb;
- unsigned long now = time(NULL);
+ timestamp_t now = time(NULL);
int i, status, do_all;
int explicit_expiry = 0;
unsigned int flags = 0;
@@ -616,7 +616,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
}
static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct expire_reflog_policy_cb *cb = cb_data;
diff --git a/builtin/remote.c b/builtin/remote.c
index 9054e2858e..f1a88fe265 100644
--- a/builtin/remote.c
+++ b/builtin/remote.c
@@ -786,7 +786,7 @@ static int rm(int argc, const char **argv)
strbuf_release(&buf);
if (!result)
- result = delete_refs(&branches, REF_NODEREF);
+ result = delete_refs("remote: remove", &branches, REF_NODEREF);
string_list_clear(&branches, 0);
if (skipped.nr) {
@@ -1304,7 +1304,7 @@ static int prune_remote(const char *remote, int dry_run)
string_list_sort(&refs_to_prune);
if (!dry_run)
- result |= delete_refs(&refs_to_prune, 0);
+ result |= delete_refs("remote: prune", &refs_to_prune, 0);
for_each_string_list_item(item, &states.stale) {
const char *refname = item->util;
diff --git a/builtin/repack.c b/builtin/repack.c
index 677bc7c81a..38ba4ef825 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -155,6 +155,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
int keep_unreachable = 0;
const char *window = NULL, *window_memory = NULL;
const char *depth = NULL;
+ const char *threads = NULL;
const char *max_pack_size = NULL;
int no_reuse_delta = 0, no_reuse_object = 0;
int no_update_server_info = 0;
@@ -190,6 +191,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
N_("same as the above, but limit memory size instead of entries count")),
OPT_STRING(0, "depth", &depth, N_("n"),
N_("limits the maximum delta depth")),
+ OPT_STRING(0, "threads", &threads, N_("n"),
+ N_("limits the maximum number of threads")),
OPT_STRING(0, "max-pack-size", &max_pack_size, N_("bytes"),
N_("maximum size of each packfile")),
OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
@@ -234,6 +237,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
argv_array_pushf(&cmd.args, "--window-memory=%s", window_memory);
if (depth)
argv_array_pushf(&cmd.args, "--depth=%s", depth);
+ if (threads)
+ argv_array_pushf(&cmd.args, "--threads=%s", threads);
if (max_pack_size)
argv_array_pushf(&cmd.args, "--max-pack-size=%s", max_pack_size);
if (no_reuse_delta)
diff --git a/builtin/replace.c b/builtin/replace.c
index ab17668f43..c921bc976f 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -328,7 +328,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
struct object_id oid;
if (get_oid(argv[i], &oid) < 0)
die(_("Not a valid object name: '%s'"), argv[i]);
- lookup_commit_or_die(oid.hash, argv[i]);
+ lookup_commit_or_die(&oid, argv[i]);
strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid));
}
@@ -355,7 +355,7 @@ static void check_one_mergetag(struct commit *commit,
int i;
hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash);
- tag = lookup_tag(tag_oid.hash);
+ tag = lookup_tag(&tag_oid);
if (!tag)
die(_("bad mergetag in commit '%s'"), ref);
if (parse_tag_buffer(tag, extra->value, extra->len))
@@ -394,7 +394,7 @@ static int create_graft(int argc, const char **argv, int force)
if (get_oid(old_ref, &old) < 0)
die(_("Not a valid object name: '%s'"), old_ref);
- commit = lookup_commit_or_die(old.hash, old_ref);
+ commit = lookup_commit_or_die(&old, old_ref);
buffer = get_commit_buffer(commit, &size);
strbuf_add(&buf, buffer, size);
diff --git a/builtin/reset.c b/builtin/reset.c
index fc3b906c47..430602d102 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -21,6 +21,27 @@
#include "parse-options.h"
#include "unpack-trees.h"
#include "cache-tree.h"
+#include "submodule.h"
+#include "submodule-config.h"
+
+static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+
+static int option_parse_recurse_submodules(const struct option *opt,
+ const char *arg, int unset)
+{
+ if (unset) {
+ recurse_submodules = RECURSE_SUBMODULES_OFF;
+ return 0;
+ }
+ if (arg)
+ recurse_submodules =
+ parse_update_recurse_submodules_arg(opt->long_name,
+ arg);
+ else
+ recurse_submodules = RECURSE_SUBMODULES_ON;
+
+ return 0;
+}
static const char * const git_reset_usage[] = {
N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
@@ -84,7 +105,7 @@ static int reset_index(const struct object_id *oid, int reset_type, int quiet)
return -1;
if (reset_type == MIXED || reset_type == HARD) {
- tree = parse_tree_indirect(oid->hash);
+ tree = parse_tree_indirect(oid);
prime_cache_tree(&the_index, tree);
}
@@ -154,7 +175,7 @@ static int read_from_tree(const struct pathspec *pathspec,
opt.format_callback = update_index_from_diff;
opt.format_callback_data = &intent_to_add;
- if (do_diff_cache(tree_oid->hash, &opt))
+ if (do_diff_cache(tree_oid, &opt))
return 1;
diffcore_std(&opt);
diff_flush(&opt);
@@ -236,7 +257,6 @@ static void parse_args(struct pathspec *pathspec,
parse_pathspec(pathspec, 0,
PATHSPEC_PREFER_FULL |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP |
(patch_mode ? PATHSPEC_PREFIX_ORIGIN : 0),
prefix, argv);
}
@@ -283,6 +303,9 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
N_("reset HEAD, index and working tree"), MERGE),
OPT_SET_INT(0, "keep", &reset_type,
N_("reset HEAD but keep local changes"), KEEP),
+ { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+ "reset", "control recursive updating of submodules",
+ PARSE_OPT_OPTARG, option_parse_recurse_submodules },
OPT_BOOL('p', "patch", &patch_mode, N_("select hunks interactively")),
OPT_BOOL('N', "intent-to-add", &intent_to_add,
N_("record only the fact that removed paths will be added later")),
@@ -295,6 +318,12 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
PARSE_OPT_KEEP_DASHDASH);
parse_args(&pathspec, argv, prefix, patch_mode, &rev);
+ if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) {
+ gitmodules_config();
+ git_config(submodule_config, NULL);
+ set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON);
+ }
+
unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash);
if (unborn) {
/* reset on unborn branch: treat as reset to empty tree */
@@ -303,7 +332,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
struct commit *commit;
if (get_sha1_committish(rev, oid.hash))
die(_("Failed to resolve '%s' as a valid revision."), rev);
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
if (!commit)
die(_("Could not parse object '%s'."), rev);
oidcpy(&oid, &commit->object.oid);
@@ -311,7 +340,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
struct tree *tree;
if (get_sha1_treeish(rev, oid.hash))
die(_("Failed to resolve '%s' as a valid tree."), rev);
- tree = parse_tree_indirect(oid.hash);
+ tree = parse_tree_indirect(&oid);
if (!tree)
die(_("Could not parse object '%s'."), rev);
oidcpy(&oid, &tree->object.oid);
@@ -380,7 +409,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
update_ref_status = reset_refs(rev, &oid);
if (reset_type == HARD && !update_ref_status && !quiet)
- print_new_head_line(lookup_commit_reference(oid.hash));
+ print_new_head_line(lookup_commit_reference(&oid));
}
if (!pathspec.nr)
remove_branch_state();
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index bcf77f0b8a..718c6059c9 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -80,7 +80,7 @@ static void show_commit(struct commit *commit, void *data)
}
if (info->show_timestamp)
- printf("%lu ", commit->date);
+ printf("%"PRItime" ", commit->date);
if (info->header_prefix)
fputs(info->header_prefix, stdout);
@@ -181,7 +181,7 @@ static void finish_object(struct object *obj, const char *name, void *cb_data)
if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid))
die("missing blob object '%s'", oid_to_hex(&obj->oid));
if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
- parse_object(obj->oid.hash);
+ parse_object(&obj->oid);
}
static void show_object(struct object *obj, const char *name, void *cb_data)
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 0513330910..efdc14473b 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -121,7 +121,7 @@ static void show_with_type(int type, const char *arg)
}
/* Output a revision, only if filter allows it */
-static void show_rev(int type, const unsigned char *sha1, const char *name)
+static void show_rev(int type, const struct object_id *oid, const char *name)
{
if (!(filter & DO_REVS))
return;
@@ -129,10 +129,10 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
if ((symbolic || abbrev_ref) && name) {
if (symbolic == SHOW_SYMBOLIC_FULL || abbrev_ref) {
- unsigned char discard[20];
+ struct object_id discard;
char *full;
- switch (dwim_ref(name, strlen(name), discard, &full)) {
+ switch (dwim_ref(name, strlen(name), discard.hash, &full)) {
case 0:
/*
* Not found -- not a ref. We could
@@ -158,9 +158,9 @@ static void show_rev(int type, const unsigned char *sha1, const char *name)
}
}
else if (abbrev)
- show_with_type(type, find_unique_abbrev(sha1, abbrev));
+ show_with_type(type, find_unique_abbrev(oid->hash, abbrev));
else
- show_with_type(type, sha1_to_hex(sha1));
+ show_with_type(type, oid_to_hex(oid));
}
/* Output a flag, only if filter allows it. */
@@ -180,11 +180,11 @@ static int show_default(void)
const char *s = def;
if (s) {
- unsigned char sha1[20];
+ struct object_id oid;
def = NULL;
- if (!get_sha1(s, sha1)) {
- show_rev(NORMAL, sha1, s);
+ if (!get_oid(s, &oid)) {
+ show_rev(NORMAL, &oid, s);
return 1;
}
}
@@ -195,19 +195,19 @@ static int show_reference(const char *refname, const struct object_id *oid, int
{
if (ref_excluded(ref_excludes, refname))
return 0;
- show_rev(NORMAL, oid->hash, refname);
+ show_rev(NORMAL, oid, refname);
return 0;
}
static int anti_reference(const char *refname, const struct object_id *oid, int flag, void *cb_data)
{
- show_rev(REVERSED, oid->hash, refname);
+ show_rev(REVERSED, oid, refname);
return 0;
}
static int show_abbrev(const struct object_id *oid, void *cb_data)
{
- show_rev(NORMAL, oid->hash, NULL);
+ show_rev(NORMAL, oid, NULL);
return 0;
}
@@ -218,7 +218,7 @@ static void show_datestring(const char *flag, const char *datestr)
/* date handling requires both flags and revs */
if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
return;
- buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
+ buffer = xstrfmt("%s%"PRItime, flag, approxidate(datestr));
show(buffer);
free(buffer);
}
@@ -242,8 +242,8 @@ static int show_file(const char *arg, int output_prefix)
static int try_difference(const char *arg)
{
char *dotdot;
- unsigned char sha1[20];
- unsigned char end[20];
+ struct object_id oid;
+ struct object_id end;
const char *next;
const char *this;
int symmetric;
@@ -273,18 +273,18 @@ static int try_difference(const char *arg)
return 0;
}
- if (!get_sha1_committish(this, sha1) && !get_sha1_committish(next, end)) {
- show_rev(NORMAL, end, next);
- show_rev(symmetric ? NORMAL : REVERSED, sha1, this);
+ if (!get_sha1_committish(this, oid.hash) && !get_sha1_committish(next, end.hash)) {
+ show_rev(NORMAL, &end, next);
+ show_rev(symmetric ? NORMAL : REVERSED, &oid, this);
if (symmetric) {
struct commit_list *exclude;
struct commit *a, *b;
- a = lookup_commit_reference(sha1);
- b = lookup_commit_reference(end);
+ a = lookup_commit_reference(&oid);
+ b = lookup_commit_reference(&end);
exclude = get_merge_bases(a, b);
while (exclude) {
struct commit *commit = pop_commit(&exclude);
- show_rev(REVERSED, commit->object.oid.hash, NULL);
+ show_rev(REVERSED, &commit->object.oid, NULL);
}
}
*dotdot = '.';
@@ -297,7 +297,7 @@ static int try_difference(const char *arg)
static int try_parent_shorthands(const char *arg)
{
char *dotdot;
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *commit;
struct commit_list *parents;
int parent_number;
@@ -327,12 +327,12 @@ static int try_parent_shorthands(const char *arg)
return 0;
*dotdot = 0;
- if (get_sha1_committish(arg, sha1)) {
+ if (get_sha1_committish(arg, oid.hash)) {
*dotdot = '^';
return 0;
}
- commit = lookup_commit_reference(sha1);
+ commit = lookup_commit_reference(&oid);
if (exclude_parent &&
exclude_parent > commit_list_count(commit->parents)) {
*dotdot = '^';
@@ -340,7 +340,7 @@ static int try_parent_shorthands(const char *arg)
}
if (include_rev)
- show_rev(NORMAL, sha1, arg);
+ show_rev(NORMAL, &oid, arg);
for (parents = commit->parents, parent_number = 1;
parents;
parents = parents->next, parent_number++) {
@@ -352,7 +352,7 @@ static int try_parent_shorthands(const char *arg)
if (symbolic)
name = xstrfmt("%s^%d", arg, parent_number);
show_rev(include_parents ? NORMAL : REVERSED,
- parents->item->object.oid.hash, name);
+ &parents->item->object.oid, name);
free(name);
}
@@ -571,7 +571,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
int did_repo_setup = 0;
int has_dashdash = 0;
int output_prefix = 0;
- unsigned char sha1[20];
+ struct object_id oid;
unsigned int flags = 0;
const char *name = NULL;
struct object_context unused;
@@ -910,11 +910,11 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
name++;
type = REVERSED;
}
- if (!get_sha1_with_context(name, flags, sha1, &unused)) {
+ if (!get_sha1_with_context(name, flags, oid.hash, &unused)) {
if (verify)
revs_count++;
else
- show_rev(type, sha1, name);
+ show_rev(type, &oid, name);
continue;
}
if (verify)
@@ -929,7 +929,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
strbuf_release(&buf);
if (verify) {
if (revs_count == 1) {
- show_rev(type, sha1, name);
+ show_rev(type, &oid, name);
return 0;
} else if (revs_count == 0 && show_default())
return 0;
diff --git a/builtin/rm.c b/builtin/rm.c
index fb79dcab18..7c323d0123 100644
--- a/builtin/rm.c
+++ b/builtin/rm.c
@@ -271,8 +271,7 @@ int cmd_rm(int argc, const char **argv, const char *prefix)
die(_("index file corrupt"));
parse_pathspec(&pathspec, 0,
- PATHSPEC_PREFER_CWD |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ PATHSPEC_PREFER_CWD,
prefix, argv);
refresh_index(&the_index, REFRESH_QUIET, &pathspec, NULL, NULL);
diff --git a/builtin/show-branch.c b/builtin/show-branch.c
index 19756595d5..4a6cc6f490 100644
--- a/builtin/show-branch.c
+++ b/builtin/show-branch.c
@@ -358,7 +358,7 @@ static void sort_ref_range(int bottom, int top)
static int append_ref(const char *refname, const struct object_id *oid,
int allow_dups)
{
- struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
+ struct commit *commit = lookup_commit_reference_gently(oid, 1);
int i;
if (!commit)
@@ -735,7 +735,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
base = strtoul(reflog_base, &ep, 10);
if (*ep) {
/* Ah, that is a date spec... */
- unsigned long at;
+ timestamp_t at;
at = approxidate(reflog_base);
read_ref_at(ref, flags, at, -1, oid.hash, NULL,
NULL, NULL, &base);
@@ -746,7 +746,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
char *logmsg;
char *nth_desc;
const char *msg;
- unsigned long timestamp;
+ timestamp_t timestamp;
int tz;
if (read_ref_at(ref, flags, 0, base+i, oid.hash, &logmsg,
@@ -816,7 +816,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
MAX_REVS), MAX_REVS);
if (get_sha1(ref_name[num_rev], revkey.hash))
die(_("'%s' is not a valid ref."), ref_name[num_rev]);
- commit = lookup_commit_reference(revkey.hash);
+ commit = lookup_commit_reference(&revkey);
if (!commit)
die(_("cannot find commit %s (%s)"),
ref_name[num_rev], oid_to_hex(&revkey));
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 566a5b6a6f..8cc648d85b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -233,8 +233,7 @@ static int module_list_compute(int argc, const char **argv,
int i, result = 0;
char *ps_matched = NULL;
parse_pathspec(pathspec, 0,
- PATHSPEC_PREFER_FULL |
- PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP,
+ PATHSPEC_PREFER_FULL,
prefix, argv);
if (pathspec->nr)
diff --git a/builtin/tag.c b/builtin/tag.c
index bdf1e88e93..1f74a56db7 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -66,7 +66,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
}
typedef int (*each_tag_name_fn)(const char *name, const char *ref,
- const unsigned char *sha1, const void *cb_data);
+ const struct object_id *oid, const void *cb_data);
static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
const void *cb_data)
@@ -74,17 +74,17 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
const char **p;
struct strbuf ref = STRBUF_INIT;
int had_error = 0;
- unsigned char sha1[20];
+ struct object_id oid;
for (p = argv; *p; p++) {
strbuf_reset(&ref);
strbuf_addf(&ref, "refs/tags/%s", *p);
- if (read_ref(ref.buf, sha1)) {
+ if (read_ref(ref.buf, oid.hash)) {
error(_("tag '%s' not found."), *p);
had_error = 1;
continue;
}
- if (fn(*p, ref.buf, sha1, cb_data))
+ if (fn(*p, ref.buf, &oid, cb_data))
had_error = 1;
}
strbuf_release(&ref);
@@ -92,16 +92,16 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
}
static int delete_tag(const char *name, const char *ref,
- const unsigned char *sha1, const void *cb_data)
+ const struct object_id *oid, const void *cb_data)
{
- if (delete_ref(NULL, ref, sha1, 0))
+ if (delete_ref(NULL, ref, oid->hash, 0))
return 1;
- printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
+ printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
return 0;
}
static int verify_tag(const char *name, const char *ref,
- const unsigned char *sha1, const void *cb_data)
+ const struct object_id *oid, const void *cb_data)
{
int flags;
const char *fmt_pretty = cb_data;
@@ -110,11 +110,11 @@ static int verify_tag(const char *name, const char *ref,
if (fmt_pretty)
flags = GPG_VERIFY_OMIT_STATUS;
- if (gpg_verify_tag(sha1, name, flags))
+ if (gpg_verify_tag(oid->hash, name, flags))
return -1;
if (fmt_pretty)
- pretty_print_ref(name, sha1, fmt_pretty);
+ pretty_print_ref(name, oid->hash, fmt_pretty);
return 0;
}
@@ -182,13 +182,13 @@ static int git_tag_config(const char *var, const char *value, void *cb)
return git_default_config(var, value, cb);
}
-static void write_tag_body(int fd, const unsigned char *sha1)
+static void write_tag_body(int fd, const struct object_id *oid)
{
unsigned long size;
enum object_type type;
char *buf, *sp;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf)
return;
/* skip header */
@@ -204,11 +204,11 @@ static void write_tag_body(int fd, const unsigned char *sha1)
free(buf);
}
-static int build_tag_object(struct strbuf *buf, int sign, unsigned char *result)
+static int build_tag_object(struct strbuf *buf, int sign, struct object_id *result)
{
if (sign && do_sign(buf) < 0)
return error(_("unable to sign the tag"));
- if (write_sha1_file(buf->buf, buf->len, tag_type, result) < 0)
+ if (write_sha1_file(buf->buf, buf->len, tag_type, result->hash) < 0)
return error(_("unable to write tag file"));
return 0;
}
@@ -223,15 +223,15 @@ struct create_tag_options {
} cleanup_mode;
};
-static void create_tag(const unsigned char *object, const char *tag,
+static void create_tag(const struct object_id *object, const char *tag,
struct strbuf *buf, struct create_tag_options *opt,
- unsigned char *prev, unsigned char *result)
+ struct object_id *prev, struct object_id *result)
{
enum object_type type;
struct strbuf header = STRBUF_INIT;
char *path = NULL;
- type = sha1_object_info(object, NULL);
+ type = sha1_object_info(object->hash, NULL);
if (type <= OBJ_NONE)
die(_("bad object type."));
@@ -240,7 +240,7 @@ static void create_tag(const unsigned char *object, const char *tag,
"type %s\n"
"tag %s\n"
"tagger %s\n\n",
- sha1_to_hex(object),
+ oid_to_hex(object),
typename(type),
tag,
git_committer_info(IDENT_STRICT));
@@ -254,7 +254,7 @@ static void create_tag(const unsigned char *object, const char *tag,
if (fd < 0)
die_errno(_("could not create file '%s'"), path);
- if (!is_null_sha1(prev)) {
+ if (!is_null_oid(prev)) {
write_tag_body(fd, prev);
} else {
struct strbuf buf = STRBUF_INIT;
@@ -296,7 +296,7 @@ static void create_tag(const unsigned char *object, const char *tag,
}
}
-static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
+static void create_reflog_msg(const struct object_id *oid, struct strbuf *sb)
{
enum object_type type;
struct commit *c;
@@ -310,17 +310,17 @@ static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
strbuf_addstr(sb, rla);
} else {
strbuf_addstr(sb, "tag: tagging ");
- strbuf_add_unique_abbrev(sb, sha1, DEFAULT_ABBREV);
+ strbuf_add_unique_abbrev(sb, oid->hash, DEFAULT_ABBREV);
}
strbuf_addstr(sb, " (");
- type = sha1_object_info(sha1, NULL);
+ type = sha1_object_info(oid->hash, NULL);
switch (type) {
default:
strbuf_addstr(sb, "object of unknown type");
break;
case OBJ_COMMIT:
- if ((buf = read_sha1_file(sha1, &type, &size)) != NULL) {
+ if ((buf = read_sha1_file(oid->hash, &type, &size)) != NULL) {
subject_len = find_commit_subject(buf, &subject_start);
strbuf_insert(sb, sb->len, subject_start, subject_len);
} else {
@@ -328,7 +328,7 @@ static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
}
free(buf);
- if ((c = lookup_commit_reference(sha1)) != NULL)
+ if ((c = lookup_commit_reference(oid)) != NULL)
strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
break;
case OBJ_TREE:
@@ -378,7 +378,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
struct strbuf buf = STRBUF_INIT;
struct strbuf ref = STRBUF_INIT;
struct strbuf reflog_msg = STRBUF_INIT;
- unsigned char object[20], prev[20];
+ struct object_id object, prev;
const char *object_ref, *tag;
struct create_tag_options opt;
char *cleanup_arg = NULL;
@@ -528,14 +528,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
if (argc > 2)
die(_("too many params"));
- if (get_sha1(object_ref, object))
+ if (get_oid(object_ref, &object))
die(_("Failed to resolve '%s' as a valid ref."), object_ref);
if (strbuf_check_tag_ref(&ref, tag))
die(_("'%s' is not a valid tag name."), tag);
- if (read_ref(ref.buf, prev))
- hashclr(prev);
+ if (read_ref(ref.buf, prev.hash))
+ oidclr(&prev);
else if (!force)
die(_("tag '%s' already exists"), tag);
@@ -550,24 +550,24 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
else
die(_("Invalid cleanup mode %s"), cleanup_arg);
- create_reflog_msg(object, &reflog_msg);
+ create_reflog_msg(&object, &reflog_msg);
if (create_tag_object) {
if (force_sign_annotate && !annotate)
opt.sign = 1;
- create_tag(object, tag, &buf, &opt, prev, object);
+ create_tag(&object, tag, &buf, &opt, &prev, &object);
}
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, ref.buf, object, prev,
+ ref_transaction_update(transaction, ref.buf, object.hash, prev.hash,
create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
reflog_msg.buf, &err) ||
ref_transaction_commit(transaction, &err))
die("%s", err.buf);
ref_transaction_free(transaction);
- if (force && !is_null_sha1(prev) && hashcmp(prev, object))
- printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev, DEFAULT_ABBREV));
+ if (force && !is_null_oid(&prev) && oidcmp(&prev, &object))
+ printf(_("Updated tag '%s' (was %s)\n"), tag, find_unique_abbrev(prev.hash, DEFAULT_ABBREV));
strbuf_release(&err);
strbuf_release(&buf);
diff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c
index 4532aa0831..8bc9997767 100644
--- a/builtin/unpack-objects.c
+++ b/builtin/unpack-objects.c
@@ -127,7 +127,7 @@ static void *get_data(unsigned long size)
}
struct delta_info {
- unsigned char base_sha1[20];
+ struct object_id base_oid;
unsigned nr;
off_t base_offset;
unsigned long size;
@@ -137,13 +137,13 @@ struct delta_info {
static struct delta_info *delta_list;
-static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
+static void add_delta_to_list(unsigned nr, const struct object_id *base_oid,
off_t base_offset,
void *delta, unsigned long size)
{
struct delta_info *info = xmalloc(sizeof(*info));
- hashcpy(info->base_sha1, base_sha1);
+ oidcpy(&info->base_oid, base_oid);
info->base_offset = base_offset;
info->size = size;
info->delta = delta;
@@ -154,7 +154,7 @@ static void add_delta_to_list(unsigned nr, unsigned const char *base_sha1,
struct obj_info {
off_t offset;
- unsigned char sha1[20];
+ struct object_id oid;
struct object *obj;
};
@@ -170,9 +170,9 @@ static unsigned nr_objects;
*/
static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
{
- unsigned char sha1[20];
+ struct object_id oid;
- if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
+ if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), oid.hash) < 0)
die("failed to write object %s", oid_to_hex(&obj->oid));
obj->flags |= FLAG_WRITTEN;
}
@@ -237,19 +237,19 @@ static void write_object(unsigned nr, enum object_type type,
void *buf, unsigned long size)
{
if (!strict) {
- if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+ if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
free(buf);
obj_list[nr].obj = NULL;
} else if (type == OBJ_BLOB) {
struct blob *blob;
- if (write_sha1_file(buf, size, typename(type), obj_list[nr].sha1) < 0)
+ if (write_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash) < 0)
die("failed to write object");
added_object(nr, type, buf, size);
free(buf);
- blob = lookup_blob(obj_list[nr].sha1);
+ blob = lookup_blob(&obj_list[nr].oid);
if (blob)
blob->object.flags |= FLAG_WRITTEN;
else
@@ -258,9 +258,10 @@ static void write_object(unsigned nr, enum object_type type,
} else {
struct object *obj;
int eaten;
- hash_sha1_file(buf, size, typename(type), obj_list[nr].sha1);
+ hash_sha1_file(buf, size, typename(type), obj_list[nr].oid.hash);
added_object(nr, type, buf, size);
- obj = parse_object_buffer(obj_list[nr].sha1, type, size, buf, &eaten);
+ obj = parse_object_buffer(&obj_list[nr].oid, type, size, buf,
+ &eaten);
if (!obj)
die("invalid %s", typename(type));
add_object_buffer(obj, buf, size);
@@ -296,7 +297,7 @@ static void added_object(unsigned nr, enum object_type type,
struct delta_info *info;
while ((info = *p) != NULL) {
- if (!hashcmp(info->base_sha1, obj_list[nr].sha1) ||
+ if (!oidcmp(&info->base_oid, &obj_list[nr].oid) ||
info->base_offset == obj_list[nr].offset) {
*p = info->next;
p = &delta_list;
@@ -320,12 +321,12 @@ static void unpack_non_delta_entry(enum object_type type, unsigned long size,
free(buf);
}
-static int resolve_against_held(unsigned nr, const unsigned char *base,
+static int resolve_against_held(unsigned nr, const struct object_id *base,
void *delta_data, unsigned long delta_size)
{
struct object *obj;
struct obj_buffer *obj_buffer;
- obj = lookup_object(base);
+ obj = lookup_object(base->hash);
if (!obj)
return 0;
obj_buffer = lookup_object_buffer(obj);
@@ -341,25 +342,25 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
{
void *delta_data, *base;
unsigned long base_size;
- unsigned char base_sha1[20];
+ struct object_id base_oid;
if (type == OBJ_REF_DELTA) {
- hashcpy(base_sha1, fill(20));
- use(20);
+ hashcpy(base_oid.hash, fill(GIT_SHA1_RAWSZ));
+ use(GIT_SHA1_RAWSZ);
delta_data = get_data(delta_size);
if (dry_run || !delta_data) {
free(delta_data);
return;
}
- if (has_sha1_file(base_sha1))
+ if (has_object_file(&base_oid))
; /* Ok we have this one */
- else if (resolve_against_held(nr, base_sha1,
+ else if (resolve_against_held(nr, &base_oid,
delta_data, delta_size))
return; /* we are done */
else {
/* cannot resolve yet --- queue it */
- hashclr(obj_list[nr].sha1);
- add_delta_to_list(nr, base_sha1, 0, delta_data, delta_size);
+ oidclr(&obj_list[nr].oid);
+ add_delta_to_list(nr, &base_oid, 0, delta_data, delta_size);
return;
}
} else {
@@ -399,8 +400,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
} else if (base_offset > obj_list[mid].offset) {
lo = mid + 1;
} else {
- hashcpy(base_sha1, obj_list[mid].sha1);
- base_found = !is_null_sha1(base_sha1);
+ oidcpy(&base_oid, &obj_list[mid].oid);
+ base_found = !is_null_oid(&base_oid);
break;
}
}
@@ -409,19 +410,19 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,
* The delta base object is itself a delta that
* has not been resolved yet.
*/
- hashclr(obj_list[nr].sha1);
- add_delta_to_list(nr, null_sha1, base_offset, delta_data, delta_size);
+ oidclr(&obj_list[nr].oid);
+ add_delta_to_list(nr, &null_oid, base_offset, delta_data, delta_size);
return;
}
}
- if (resolve_against_held(nr, base_sha1, delta_data, delta_size))
+ if (resolve_against_held(nr, &base_oid, delta_data, delta_size))
return;
- base = read_sha1_file(base_sha1, &type, &base_size);
+ base = read_sha1_file(base_oid.hash, &type, &base_size);
if (!base) {
error("failed to read delta-pack base object %s",
- sha1_to_hex(base_sha1));
+ oid_to_hex(&base_oid));
if (!recover)
exit(1);
has_errors = 1;
@@ -505,7 +506,7 @@ static void unpack_all(void)
int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
{
int i;
- unsigned char sha1[20];
+ struct object_id oid;
check_replace_refs = 0;
@@ -566,12 +567,12 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)
git_SHA1_Init(&ctx);
unpack_all();
git_SHA1_Update(&ctx, buffer, offset);
- git_SHA1_Final(sha1, &ctx);
+ git_SHA1_Final(oid.hash, &ctx);
if (strict)
write_rest();
- if (hashcmp(fill(20), sha1))
+ if (hashcmp(fill(GIT_SHA1_RAWSZ), oid.hash))
die("final sha1 did not match");
- use(20);
+ use(GIT_SHA1_RAWSZ);
/* Write the last part of the buffer to stdout */
while (len) {
diff --git a/builtin/verify-commit.c b/builtin/verify-commit.c
index 38bedf8f9f..05b734e6d1 100644
--- a/builtin/verify-commit.c
+++ b/builtin/verify-commit.c
@@ -18,14 +18,14 @@ static const char * const verify_commit_usage[] = {
NULL
};
-static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned long size, unsigned flags)
+static int run_gpg_verify(const struct object_id *oid, const char *buf, unsigned long size, unsigned flags)
{
struct signature_check signature_check;
int ret;
memset(&signature_check, 0, sizeof(signature_check));
- ret = check_commit_signature(lookup_commit(sha1), &signature_check);
+ ret = check_commit_signature(lookup_commit(oid), &signature_check);
print_signature_buffer(&signature_check, flags);
signature_check_clear(&signature_check);
@@ -35,22 +35,22 @@ static int run_gpg_verify(const unsigned char *sha1, const char *buf, unsigned l
static int verify_commit(const char *name, unsigned flags)
{
enum object_type type;
- unsigned char sha1[20];
+ struct object_id oid;
char *buf;
unsigned long size;
int ret;
- if (get_sha1(name, sha1))
+ if (get_oid(name, &oid))
return error("commit '%s' not found.", name);
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid.hash, &type, &size);
if (!buf)
return error("%s: unable to read file.", name);
if (type != OBJ_COMMIT)
return error("%s: cannot verify a non-commit object of type %s.",
name, typename(type));
- ret = run_gpg_verify(sha1, buf, size, flags);
+ ret = run_gpg_verify(&oid, buf, size, flags);
free(buf);
return ret;
diff --git a/builtin/worktree.c b/builtin/worktree.c
index ff5dfd2b10..793306ea51 100644
--- a/builtin/worktree.c
+++ b/builtin/worktree.c
@@ -31,7 +31,7 @@ struct add_opts {
static int show_only;
static int verbose;
-static unsigned long expire;
+static timestamp_t expire;
static int prune_worktree(const char *id, struct strbuf *reason)
{
@@ -131,7 +131,7 @@ static int prune(int ac, const char **av, const char *prefix)
OPT_END()
};
- expire = ULONG_MAX;
+ expire = TIME_MAX;
ac = parse_options(ac, av, prefix, options, worktree_usage, 0);
if (ac)
usage_with_options(worktree_usage, options);
diff --git a/bulk-checkin.c b/bulk-checkin.c
index ddb6070c4c..5be7ce5c73 100644
--- a/bulk-checkin.c
+++ b/bulk-checkin.c
@@ -69,7 +69,7 @@ static int already_written(struct bulk_checkin_state *state, unsigned char sha1[
/* Might want to keep the list sorted */
for (i = 0; i < state->nr_written; i++)
- if (!hashcmp(state->written[i]->sha1, sha1))
+ if (!hashcmp(state->written[i]->oid.hash, sha1))
return 1;
/* This is a new object we need to keep */
@@ -242,7 +242,7 @@ static int deflate_to_pack(struct bulk_checkin_state *state,
state->offset = checkpoint.offset;
free(idx);
} else {
- hashcpy(idx->sha1, result_sha1);
+ hashcpy(idx->oid.hash, result_sha1);
ALLOC_GROW(state->written,
state->nr_written + 1,
state->alloc_written);
diff --git a/bundle.c b/bundle.c
index bbf4efa0a0..d15db03c84 100644
--- a/bundle.c
+++ b/bundle.c
@@ -12,11 +12,11 @@
static const char bundle_signature[] = "# v2 git bundle\n";
-static void add_to_ref_list(const unsigned char *sha1, const char *name,
+static void add_to_ref_list(const struct object_id *oid, const char *name,
struct ref_list *list)
{
ALLOC_GROW(list->list, list->nr + 1, list->alloc);
- hashcpy(list->list[list->nr].sha1, sha1);
+ oidcpy(&list->list[list->nr].oid, oid);
list->list[list->nr].name = xstrdup(name);
list->nr++;
}
@@ -40,8 +40,9 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
/* The bundle header ends with an empty line */
while (!strbuf_getwholeline_fd(&buf, fd, '\n') &&
buf.len && buf.buf[0] != '\n') {
- unsigned char sha1[20];
+ struct object_id oid;
int is_prereq = 0;
+ const char *p;
if (*buf.buf == '-') {
is_prereq = 1;
@@ -54,9 +55,9 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
* Prerequisites have object name that is optionally
* followed by SP and subject line.
*/
- if (get_sha1_hex(buf.buf, sha1) ||
- (buf.len > 40 && !isspace(buf.buf[40])) ||
- (!is_prereq && buf.len <= 40)) {
+ if (parse_oid_hex(buf.buf, &oid, &p) ||
+ (*p && !isspace(*p)) ||
+ (!is_prereq && !*p)) {
if (report_path)
error(_("unrecognized header: %s%s (%d)"),
(is_prereq ? "-" : ""), buf.buf, (int)buf.len);
@@ -64,9 +65,9 @@ static int parse_bundle_header(int fd, struct bundle_header *header,
break;
} else {
if (is_prereq)
- add_to_ref_list(sha1, "", &header->prerequisites);
+ add_to_ref_list(&oid, "", &header->prerequisites);
else
- add_to_ref_list(sha1, buf.buf + 41, &header->references);
+ add_to_ref_list(&oid, p + 1, &header->references);
}
}
@@ -115,7 +116,7 @@ static int list_refs(struct ref_list *r, int argc, const char **argv)
if (j == argc)
continue;
}
- printf("%s %s\n", sha1_to_hex(r->list[i].sha1),
+ printf("%s %s\n", oid_to_hex(&r->list[i].oid),
r->list[i].name);
}
return 0;
@@ -141,7 +142,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
init_revisions(&revs, NULL);
for (i = 0; i < p->nr; i++) {
struct ref_list_entry *e = p->list + i;
- struct object *o = parse_object(e->sha1);
+ struct object *o = parse_object(&e->oid);
if (o) {
o->flags |= PREREQ_MARK;
add_pending_object(&revs, o, e->name);
@@ -149,7 +150,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
}
if (++ret == 1)
error("%s", message);
- error("%s %s", sha1_to_hex(e->sha1), e->name);
+ error("%s %s", oid_to_hex(&e->oid), e->name);
}
if (revs.pending.nr != p->nr)
return ret;
@@ -211,7 +212,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
unsigned long size;
enum object_type type;
char *buf = NULL, *line, *lineend;
- unsigned long date;
+ timestamp_t date;
int result = 1;
if (revs->max_age == -1 && revs->min_age == -1)
@@ -227,7 +228,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
line = memchr(line, '>', lineend ? lineend - line : buf + size - line);
if (!line++)
goto out;
- date = strtoul(line, NULL, 10);
+ date = parse_timestamp(line, NULL, 10);
result = (revs->max_age == -1 || revs->max_age < date) &&
(revs->min_age == -1 || revs->min_age > date);
out:
@@ -285,16 +286,18 @@ static int compute_and_write_prerequisites(int bundle_fd,
return -1;
rls_fout = xfdopen(rls.out, "r");
while (strbuf_getwholeline(&buf, rls_fout, '\n') != EOF) {
- unsigned char sha1[20];
+ struct object_id oid;
if (buf.len > 0 && buf.buf[0] == '-') {
write_or_die(bundle_fd, buf.buf, buf.len);
- if (!get_sha1_hex(buf.buf + 1, sha1)) {
- struct object *object = parse_object_or_die(sha1, buf.buf);
+ if (!get_oid_hex(buf.buf + 1, &oid)) {
+ struct object *object = parse_object_or_die(&oid,
+ buf.buf);
object->flags |= UNINTERESTING;
add_pending_object(revs, object, buf.buf);
}
- } else if (!get_sha1_hex(buf.buf, sha1)) {
- struct object *object = parse_object_or_die(sha1, buf.buf);
+ } else if (!get_oid_hex(buf.buf, &oid)) {
+ struct object *object = parse_object_or_die(&oid,
+ buf.buf);
object->flags |= SHOWN;
}
}
@@ -366,7 +369,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
* in terms of a tag (e.g. v2.0 from the range
* "v1.0..v2.0")?
*/
- struct commit *one = lookup_commit_reference(oid.hash);
+ struct commit *one = lookup_commit_reference(&oid);
struct object *obj;
if (e->item == &(one->object)) {
@@ -378,7 +381,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
* end up triggering "empty bundle"
* error.
*/
- obj = parse_object_or_die(oid.hash, e->name);
+ obj = parse_object_or_die(&oid, e->name);
obj->flags |= SHOWN;
add_pending_object(revs, obj, e->name);
}
diff --git a/bundle.h b/bundle.h
index 1584e4d821..e9a4cb6a74 100644
--- a/bundle.h
+++ b/bundle.h
@@ -1,10 +1,12 @@
#ifndef BUNDLE_H
#define BUNDLE_H
+#include "cache.h"
+
struct ref_list {
unsigned int nr, alloc;
struct ref_list_entry {
- unsigned char sha1[20];
+ struct object_id oid;
char *name;
} *list;
};
diff --git a/cache-tree.c b/cache-tree.c
index 345ea35963..ec23d8c03d 100644
--- a/cache-tree.c
+++ b/cache-tree.c
@@ -225,7 +225,7 @@ int cache_tree_fully_valid(struct cache_tree *it)
int i;
if (!it)
return 0;
- if (it->entry_count < 0 || !has_sha1_file(it->sha1))
+ if (it->entry_count < 0 || !has_sha1_file(it->oid.hash))
return 0;
for (i = 0; i < it->subtree_nr; i++) {
if (!cache_tree_fully_valid(it->down[i]->cache_tree))
@@ -253,7 +253,7 @@ static int update_one(struct cache_tree *it,
*skip_count = 0;
- if (0 <= it->entry_count && has_sha1_file(it->sha1))
+ if (0 <= it->entry_count && has_sha1_file(it->oid.hash))
return it->entry_count;
/*
@@ -340,7 +340,7 @@ static int update_one(struct cache_tree *it,
die("cache-tree.c: '%.*s' in '%s' not found",
entlen, path + baselen, path);
i += sub->count;
- sha1 = sub->cache_tree->sha1;
+ sha1 = sub->cache_tree->oid.hash;
mode = S_IFDIR;
contains_ita = sub->cache_tree->entry_count < 0;
if (contains_ita) {
@@ -354,7 +354,9 @@ static int update_one(struct cache_tree *it,
entlen = pathlen - baselen;
i++;
}
- if (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1)) {
+
+ if (is_null_sha1(sha1) ||
+ (mode != S_IFGITLINK && !missing_ok && !has_sha1_file(sha1))) {
strbuf_release(&buffer);
if (expected_missing)
return -1;
@@ -402,12 +404,13 @@ static int update_one(struct cache_tree *it,
unsigned char sha1[20];
hash_sha1_file(buffer.buf, buffer.len, tree_type, sha1);
if (has_sha1_file(sha1))
- hashcpy(it->sha1, sha1);
+ hashcpy(it->oid.hash, sha1);
else
to_invalidate = 1;
} else if (dryrun)
- hash_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1);
- else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->sha1)) {
+ hash_sha1_file(buffer.buf, buffer.len, tree_type,
+ it->oid.hash);
+ else if (write_sha1_file(buffer.buf, buffer.len, tree_type, it->oid.hash)) {
strbuf_release(&buffer);
return -1;
}
@@ -417,7 +420,7 @@ static int update_one(struct cache_tree *it,
#if DEBUG
fprintf(stderr, "cache-tree update-one (%d ent, %d subtree) %s\n",
it->entry_count, it->subtree_nr,
- sha1_to_hex(it->sha1));
+ oid_to_hex(&it->oid));
#endif
return i;
}
@@ -457,14 +460,14 @@ static void write_one(struct strbuf *buffer, struct cache_tree *it,
if (0 <= it->entry_count)
fprintf(stderr, "cache-tree <%.*s> (%d ent, %d subtree) %s\n",
pathlen, path, it->entry_count, it->subtree_nr,
- sha1_to_hex(it->sha1));
+ oid_to_hex(&it->oid));
else
fprintf(stderr, "cache-tree <%.*s> (%d subtree) invalid\n",
pathlen, path, it->subtree_nr);
#endif
if (0 <= it->entry_count) {
- strbuf_add(buffer, it->sha1, 20);
+ strbuf_add(buffer, it->oid.hash, 20);
}
for (i = 0; i < it->subtree_nr; i++) {
struct cache_tree_sub *down = it->down[i];
@@ -521,7 +524,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
if (0 <= it->entry_count) {
if (size < 20)
goto free_return;
- hashcpy(it->sha1, (const unsigned char*)buf);
+ hashcpy(it->oid.hash, (const unsigned char*)buf);
buf += 20;
size -= 20;
}
@@ -530,7 +533,7 @@ static struct cache_tree *read_one(const char **buffer, unsigned long *size_p)
if (0 <= it->entry_count)
fprintf(stderr, "cache-tree <%s> (%d ent, %d subtree) %s\n",
*buffer, it->entry_count, subtree_nr,
- sha1_to_hex(it->sha1));
+ oid_to_hex(&it->oid));
else
fprintf(stderr, "cache-tree <%s> (%d subtrees) invalid\n",
*buffer, subtree_nr);
@@ -641,10 +644,10 @@ int write_index_as_tree(unsigned char *sha1, struct index_state *index_state, co
subtree = cache_tree_find(index_state->cache_tree, prefix);
if (!subtree)
return WRITE_TREE_PREFIX_ERROR;
- hashcpy(sha1, subtree->sha1);
+ hashcpy(sha1, subtree->oid.hash);
}
else
- hashcpy(sha1, index_state->cache_tree->sha1);
+ hashcpy(sha1, index_state->cache_tree->oid.hash);
if (0 <= newfd)
rollback_lock_file(lock_file);
@@ -663,7 +666,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
struct name_entry entry;
int cnt;
- hashcpy(it->sha1, tree->object.oid.hash);
+ oidcpy(&it->oid, &tree->object.oid);
init_tree_desc(&desc, tree->buffer, tree->size);
cnt = 0;
while (tree_entry(&desc, &entry)) {
@@ -671,7 +674,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
cnt++;
else {
struct cache_tree_sub *sub;
- struct tree *subtree = lookup_tree(entry.oid->hash);
+ struct tree *subtree = lookup_tree(entry.oid);
if (!subtree->object.parsed)
parse_tree(subtree);
sub = cache_tree_sub(it, entry.path);
@@ -718,7 +721,7 @@ int cache_tree_matches_traversal(struct cache_tree *root,
it = find_cache_tree_from_traversal(root, info);
it = cache_tree_find(it, ent->path);
- if (it && it->entry_count > 0 && !hashcmp(ent->oid->hash, it->sha1))
+ if (it && it->entry_count > 0 && !oidcmp(ent->oid, &it->oid))
return it->entry_count;
return 0;
}
diff --git a/cache-tree.h b/cache-tree.h
index 41c574663a..f7b9cab7ee 100644
--- a/cache-tree.h
+++ b/cache-tree.h
@@ -1,6 +1,7 @@
#ifndef CACHE_TREE_H
#define CACHE_TREE_H
+#include "cache.h"
#include "tree.h"
#include "tree-walk.h"
@@ -15,7 +16,7 @@ struct cache_tree_sub {
struct cache_tree {
int entry_count; /* negative means "invalid" */
- unsigned char sha1[20];
+ struct object_id oid;
int subtree_nr;
int subtree_alloc;
struct cache_tree_sub **down;
diff --git a/cache.h b/cache.h
index e1f0e182ad..4d92aae0e8 100644
--- a/cache.h
+++ b/cache.h
@@ -597,6 +597,7 @@ extern int read_index_unmerged(struct index_state *);
#define CLOSE_LOCK (1 << 1)
extern int write_locked_index(struct index_state *, struct lock_file *lock, unsigned flags);
extern int discard_index(struct index_state *);
+extern void move_index_extensions(struct index_state *dst, struct index_state *src);
extern int unmerged_index(const struct index_state *);
extern int verify_path(const char *path);
extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
@@ -1333,13 +1334,18 @@ static inline int hex2chr(const char *s)
struct object_context {
unsigned char tree[20];
- char path[PATH_MAX];
unsigned mode;
/*
* symlink_path is only used by get_tree_entry_follow_symlinks,
* and only for symlinks that point outside the repository.
*/
struct strbuf symlink_path;
+ /*
+ * If GET_SHA1_RECORD_PATH is set, this will record path (if any)
+ * found when resolving the name. The caller is responsible for
+ * releasing the memory.
+ */
+ char *path;
};
#define GET_SHA1_QUIETLY 01
@@ -1349,6 +1355,7 @@ struct object_context {
#define GET_SHA1_TREEISH 020
#define GET_SHA1_BLOB 040
#define GET_SHA1_FOLLOW_SYMLINKS 0100
+#define GET_SHA1_RECORD_PATH 0200
#define GET_SHA1_ONLY_TO_DIE 04000
#define GET_SHA1_DISAMBIGUATORS \
@@ -1363,7 +1370,7 @@ extern int get_sha1_tree(const char *str, unsigned char *sha1);
extern int get_sha1_treeish(const char *str, unsigned char *sha1);
extern int get_sha1_blob(const char *str, unsigned char *sha1);
extern void maybe_die_on_misspelt_object_name(const char *name, const char *prefix);
-extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc);
+extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc);
extern int get_oid(const char *str, struct object_id *oid);
@@ -1479,18 +1486,18 @@ struct date_mode {
#define DATE_MODE(t) date_mode_from_type(DATE_##t)
struct date_mode *date_mode_from_type(enum date_mode_type type);
-const char *show_date(unsigned long time, int timezone, const struct date_mode *mode);
-void show_date_relative(unsigned long time, int tz, const struct timeval *now,
+const char *show_date(timestamp_t time, int timezone, const struct date_mode *mode);
+void show_date_relative(timestamp_t time, int tz, const struct timeval *now,
struct strbuf *timebuf);
int parse_date(const char *date, struct strbuf *out);
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset);
-int parse_expiry_date(const char *date, unsigned long *timestamp);
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset);
+int parse_expiry_date(const char *date, timestamp_t *timestamp);
void datestamp(struct strbuf *out);
#define approxidate(s) approxidate_careful((s), NULL)
-unsigned long approxidate_careful(const char *, int *);
-unsigned long approxidate_relative(const char *date, const struct timeval *now);
+timestamp_t approxidate_careful(const char *, int *);
+timestamp_t approxidate_relative(const char *date, const struct timeval *now);
void parse_date_format(const char *format, struct date_mode *mode);
-int date_overflows(unsigned long date);
+int date_overflows(timestamp_t date);
#define IDENT_STRICT 1
#define IDENT_NO_DATE 2
@@ -2198,8 +2205,8 @@ struct commit_list;
int try_merge_command(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 unsigned char *from,
- const unsigned char *to,
+int checkout_fast_forward(const struct object_id *from,
+ const struct object_id *to,
int overwrite_ignore);
diff --git a/ci/run-windows-build.sh b/ci/run-windows-build.sh
index d8f0d92f28..2d98f6b2f9 100755
--- a/ci/run-windows-build.sh
+++ b/ci/run-windows-build.sh
@@ -14,14 +14,33 @@ COMMIT=$2
gfwci () {
local CURL_ERROR_CODE HTTP_CODE
- exec 3>&1
+ CONTENT_FILE=$(mktemp -t "git-windows-ci-XXXXXX")
+ while test -z $HTTP_CODE
+ do
HTTP_CODE=$(curl \
-H "Authentication: Bearer $GFW_CI_TOKEN" \
--silent --retry 5 --write-out '%{HTTP_CODE}' \
- --output >(sed "$(printf '1s/^\xef\xbb\xbf//')" >cat >&3) \
+ --output >(sed "$(printf '1s/^\xef\xbb\xbf//')" >$CONTENT_FILE) \
"https://git-for-windows-ci.azurewebsites.net/api/TestNow?$1" \
)
CURL_ERROR_CODE=$?
+ # The GfW CI web app sometimes returns HTTP errors of
+ # "502 bad gateway" or "503 service unavailable".
+ # We also need to check the HTTP content because the GfW web
+ # app seems to pass through (error) results from other Azure
+ # calls with HTTP code 200.
+ # Wait a little and retry if we detect this error. More info:
+ # https://docs.microsoft.com/en-in/azure/app-service-web/app-service-web-troubleshoot-http-502-http-503
+ if test $HTTP_CODE -eq 502 ||
+ test $HTTP_CODE -eq 503 ||
+ grep "502 - Web server received an invalid response" $CONTENT_FILE >/dev/null
+ then
+ sleep 10
+ HTTP_CODE=
+ fi
+ done
+ cat $CONTENT_FILE
+ rm $CONTENT_FILE
if test $CURL_ERROR_CODE -ne 0
then
return $CURL_ERROR_CODE
@@ -61,7 +80,8 @@ do
case "$STATUS" in
inProgress|postponed|notStarted) sleep 10 ;; # continue
"completed: succeeded") RESULT="success"; break;; # success
- *) echo "Unhandled status: $STATUS"; break;; # failure
+ "completed: failed") break;; # failure
+ *) echo "Unhandled status: $STATUS"; break;; # unknown
esac
done
diff --git a/commit.c b/commit.c
index c14ddf191f..713f09feb0 100644
--- a/commit.c
+++ b/commit.c
@@ -19,38 +19,38 @@ int save_commit_buffer = 1;
const char *commit_type = "commit";
-struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
+struct commit *lookup_commit_reference_gently(const struct object_id *oid,
int quiet)
{
- struct object *obj = deref_tag(parse_object(sha1), NULL, 0);
+ struct object *obj = deref_tag(parse_object(oid), NULL, 0);
if (!obj)
return NULL;
return object_as_type(obj, OBJ_COMMIT, quiet);
}
-struct commit *lookup_commit_reference(const unsigned char *sha1)
+struct commit *lookup_commit_reference(const struct object_id *oid)
{
- return lookup_commit_reference_gently(sha1, 0);
+ return lookup_commit_reference_gently(oid, 0);
}
-struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name)
+struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name)
{
- struct commit *c = lookup_commit_reference(sha1);
+ struct commit *c = lookup_commit_reference(oid);
if (!c)
die(_("could not parse %s"), ref_name);
- if (hashcmp(sha1, c->object.oid.hash)) {
+ if (oidcmp(oid, &c->object.oid)) {
warning(_("%s %s is not a commit!"),
- ref_name, sha1_to_hex(sha1));
+ ref_name, oid_to_hex(oid));
}
return c;
}
-struct commit *lookup_commit(const unsigned char *sha1)
+struct commit *lookup_commit(const struct object_id *oid)
{
- struct object *obj = lookup_object(sha1);
+ struct object *obj = lookup_object(oid->hash);
if (!obj)
- return create_object(sha1, alloc_commit_node());
+ return create_object(oid->hash, alloc_commit_node());
return object_as_type(obj, OBJ_COMMIT, 0);
}
@@ -61,13 +61,13 @@ struct commit *lookup_commit_reference_by_name(const char *name)
if (get_sha1_committish(name, oid.hash))
return NULL;
- commit = lookup_commit_reference(oid.hash);
+ commit = lookup_commit_reference(&oid);
if (parse_commit(commit))
return NULL;
return commit;
}
-static unsigned long parse_commit_date(const char *buf, const char *tail)
+static timestamp_t parse_commit_date(const char *buf, const char *tail)
{
const char *dateptr;
@@ -90,8 +90,8 @@ static unsigned long parse_commit_date(const char *buf, const char *tail)
/* nada */;
if (buf >= tail)
return 0;
- /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
- return strtoul(dateptr, NULL, 10);
+ /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+ return parse_timestamp(dateptr, NULL, 10);
}
static struct commit_graft **commit_graft;
@@ -217,9 +217,9 @@ int for_each_commit_graft(each_commit_graft_fn fn, void *cb_data)
return ret;
}
-int unregister_shallow(const unsigned char *sha1)
+int unregister_shallow(const struct object_id *oid)
{
- int pos = commit_graft_pos(sha1);
+ int pos = commit_graft_pos(oid->hash);
if (pos < 0)
return -1;
if (pos + 1 < commit_graft_nr)
@@ -332,7 +332,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
if (get_sha1_hex(bufptr + 5, parent.hash) < 0)
return error("bad tree pointer in commit %s",
oid_to_hex(&item->object.oid));
- item->tree = lookup_tree(parent.hash);
+ item->tree = lookup_tree(&parent);
bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
pptr = &item->parents;
@@ -351,7 +351,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
*/
if (graft && (graft->nr_parent < 0 || grafts_replace_parents))
continue;
- new_parent = lookup_commit(parent.hash);
+ new_parent = lookup_commit(&parent);
if (new_parent)
pptr = &commit_list_insert(new_parent, pptr)->next;
}
@@ -359,7 +359,7 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
int i;
struct commit *new_parent;
for (i = 0; i < graft->nr_parent; i++) {
- new_parent = lookup_commit(graft->parent[i].hash);
+ new_parent = lookup_commit(&graft->parent[i]);
if (!new_parent)
continue;
pptr = &commit_list_insert(new_parent, pptr)->next;
@@ -474,8 +474,8 @@ struct commit_list * commit_list_insert_by_date(struct commit *item, struct comm
static int commit_list_compare_by_date(const void *a, const void *b)
{
- unsigned long a_date = ((const struct commit_list *)a)->item->date;
- unsigned long b_date = ((const struct commit_list *)b)->item->date;
+ timestamp_t a_date = ((const struct commit_list *)a)->item->date;
+ timestamp_t b_date = ((const struct commit_list *)b)->item->date;
if (a_date < b_date)
return 1;
if (a_date > b_date)
@@ -563,7 +563,7 @@ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
for (i = 0; i < a->nr; i++) {
object = a->objects[i].item;
- commit = lookup_commit_reference_gently(object->oid.hash, 1);
+ commit = lookup_commit_reference_gently(&object->oid, 1);
if (commit)
clear_commit_marks(commit, mark);
}
@@ -599,7 +599,7 @@ static void record_author_date(struct author_date_slab *author_date,
const char *ident_line;
size_t ident_len;
char *date_end;
- unsigned long date;
+ timestamp_t date;
ident_line = find_commit_header(buffer, "author", &ident_len);
if (!ident_line)
@@ -608,7 +608,7 @@ static void record_author_date(struct author_date_slab *author_date,
!ident.date_begin || !ident.date_end)
goto fail_exit; /* malformed "author" line */
- date = strtoul(ident.date_begin, &date_end, 10);
+ date = parse_timestamp(ident.date_begin, &date_end, 10);
if (date_end != ident.date_end)
goto fail_exit; /* malformed date */
*(author_date_slab_at(author_date, commit)) = date;
@@ -622,8 +622,8 @@ static int compare_commits_by_author_date(const void *a_, const void *b_,
{
const struct commit *a = a_, *b = b_;
struct author_date_slab *author_date = cb_data;
- unsigned long a_date = *(author_date_slab_at(author_date, a));
- unsigned long b_date = *(author_date_slab_at(author_date, b));
+ timestamp_t a_date = *(author_date_slab_at(author_date, a));
+ timestamp_t b_date = *(author_date_slab_at(author_date, b));
/* newer commits with larger date first */
if (a_date < b_date)
@@ -1590,7 +1590,7 @@ struct commit *get_merge_parent(const char *name)
struct object_id oid;
if (get_sha1(name, oid.hash))
return NULL;
- obj = parse_object(oid.hash);
+ obj = parse_object(&oid);
commit = (struct commit *)peel_to_type(name, 0, obj, OBJ_COMMIT);
if (commit && !commit->util)
set_merge_remote_desc(commit, name, obj);
diff --git a/commit.h b/commit.h
index 7b1986d5c8..4127c298cb 100644
--- a/commit.h
+++ b/commit.h
@@ -17,7 +17,7 @@ struct commit {
struct object object;
void *util;
unsigned int index;
- unsigned long date;
+ timestamp_t date;
struct commit_list *parents;
struct tree *tree;
};
@@ -45,18 +45,18 @@ enum decoration_type {
void add_name_decoration(enum decoration_type type, const char *name, struct object *obj);
const struct name_decoration *get_name_decoration(const struct object *obj);
-struct commit *lookup_commit(const unsigned char *sha1);
-struct commit *lookup_commit_reference(const unsigned char *sha1);
-struct commit *lookup_commit_reference_gently(const unsigned char *sha1,
+struct commit *lookup_commit(const struct object_id *oid);
+struct commit *lookup_commit_reference(const struct object_id *oid);
+struct commit *lookup_commit_reference_gently(const struct object_id *oid,
int quiet);
struct commit *lookup_commit_reference_by_name(const char *name);
/*
- * Look up object named by "sha1", dereference tag as necessary,
- * get a commit and return it. If "sha1" does not dereference to
+ * Look up object named by "oid", dereference tag as necessary,
+ * get a commit and return it. If "oid" does not dereference to
* a commit, use ref_name to report an error and die.
*/
-struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_name);
+struct commit *lookup_commit_or_die(const struct object_id *oid, const char *ref_name);
int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long size);
int parse_commit_gently(struct commit *item, int quiet_on_missing);
@@ -263,8 +263,8 @@ extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n,
struct oid_array;
struct ref;
-extern int register_shallow(const unsigned char *sha1);
-extern int unregister_shallow(const unsigned char *sha1);
+extern int register_shallow(const struct object_id *oid);
+extern int unregister_shallow(const struct object_id *oid);
extern int for_each_commit_graft(each_commit_graft_fn, void *);
extern int is_repository_shallow(void);
extern struct commit_list *get_shallow_commits(struct object_array *heads,
diff --git a/compat/mingw.c b/compat/mingw.c
index fe0e3ccd24..c6134f7c81 100644
--- a/compat/mingw.c
+++ b/compat/mingw.c
@@ -941,66 +941,14 @@ static const char *parse_interpreter(const char *cmd)
}
/*
- * Splits the PATH into parts.
- */
-static char **get_path_split(void)
-{
- char *p, **path, *envpath = mingw_getenv("PATH");
- int i, n = 0;
-
- if (!envpath || !*envpath)
- return NULL;
-
- envpath = xstrdup(envpath);
- p = envpath;
- while (p) {
- char *dir = p;
- p = strchr(p, ';');
- if (p) *p++ = '\0';
- if (*dir) { /* not earlier, catches series of ; */
- ++n;
- }
- }
- if (!n) {
- free(envpath);
- return NULL;
- }
-
- ALLOC_ARRAY(path, n + 1);
- p = envpath;
- i = 0;
- do {
- if (*p)
- path[i++] = xstrdup(p);
- p = p+strlen(p)+1;
- } while (i < n);
- path[i] = NULL;
-
- free(envpath);
-
- return path;
-}
-
-static void free_path_split(char **path)
-{
- char **p = path;
-
- if (!path)
- return;
-
- while (*p)
- free(*p++);
- free(path);
-}
-
-/*
* exe_only means that we only want to detect .exe files, but not scripts
* (which do not have an extension)
*/
-static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_only)
+static char *lookup_prog(const char *dir, int dirlen, const char *cmd,
+ int isexe, int exe_only)
{
char path[MAX_PATH];
- snprintf(path, sizeof(path), "%s/%s.exe", dir, cmd);
+ snprintf(path, sizeof(path), "%.*s\\%s.exe", dirlen, dir, cmd);
if (!isexe && access(path, F_OK) == 0)
return xstrdup(path);
@@ -1015,17 +963,29 @@ static char *lookup_prog(const char *dir, const char *cmd, int isexe, int exe_on
* Determines the absolute path of cmd using the split path in path.
* If cmd contains a slash or backslash, no lookup is performed.
*/
-static char *path_lookup(const char *cmd, char **path, int exe_only)
+static char *path_lookup(const char *cmd, int exe_only)
{
+ const char *path;
char *prog = NULL;
int len = strlen(cmd);
int isexe = len >= 4 && !strcasecmp(cmd+len-4, ".exe");
if (strchr(cmd, '/') || strchr(cmd, '\\'))
- prog = xstrdup(cmd);
+ return xstrdup(cmd);
+
+ path = mingw_getenv("PATH");
+ if (!path)
+ return NULL;
- while (!prog && *path)
- prog = lookup_prog(*path++, cmd, isexe, exe_only);
+ while (!prog) {
+ const char *sep = strchrnul(path, ';');
+ int dirlen = sep - path;
+ if (dirlen)
+ prog = lookup_prog(path, dirlen, cmd, isexe, exe_only);
+ if (!*sep)
+ break;
+ path = sep + 1;
+ }
return prog;
}
@@ -1192,8 +1152,7 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
int fhin, int fhout, int fherr)
{
pid_t pid;
- char **path = get_path_split();
- char *prog = path_lookup(cmd, path, 0);
+ char *prog = path_lookup(cmd, 0);
if (!prog) {
errno = ENOENT;
@@ -1204,7 +1163,7 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
if (interpr) {
const char *argv0 = argv[0];
- char *iprog = path_lookup(interpr, path, 1);
+ char *iprog = path_lookup(interpr, 1);
argv[0] = prog;
if (!iprog) {
errno = ENOENT;
@@ -1222,21 +1181,18 @@ pid_t mingw_spawnvpe(const char *cmd, const char **argv, char **deltaenv,
fhin, fhout, fherr);
free(prog);
}
- free_path_split(path);
return pid;
}
static int try_shell_exec(const char *cmd, char *const *argv)
{
const char *interpr = parse_interpreter(cmd);
- char **path;
char *prog;
int pid = 0;
if (!interpr)
return 0;
- path = get_path_split();
- prog = path_lookup(interpr, path, 1);
+ prog = path_lookup(interpr, 1);
if (prog) {
int argc = 0;
const char **argv2;
@@ -1255,7 +1211,6 @@ static int try_shell_exec(const char *cmd, char *const *argv)
free(prog);
free(argv2);
}
- free_path_split(path);
return pid;
}
@@ -1277,8 +1232,7 @@ int mingw_execv(const char *cmd, char *const *argv)
int mingw_execvp(const char *cmd, char *const *argv)
{
- char **path = get_path_split();
- char *prog = path_lookup(cmd, path, 0);
+ char *prog = path_lookup(cmd, 0);
if (prog) {
mingw_execv(prog, argv);
@@ -1286,7 +1240,6 @@ int mingw_execvp(const char *cmd, char *const *argv)
} else
errno = ENOENT;
- free_path_split(path);
return -1;
}
diff --git a/config.c b/config.c
index a30056ec7e..146cb3452a 100644
--- a/config.c
+++ b/config.c
@@ -214,6 +214,7 @@ static int include_by_gitdir(const struct config_options *opts,
struct strbuf pattern = STRBUF_INIT;
int ret = 0, prefix;
const char *git_dir;
+ int already_tried_absolute = 0;
if (opts->git_dir)
git_dir = opts->git_dir;
@@ -226,6 +227,7 @@ static int include_by_gitdir(const struct config_options *opts,
strbuf_add(&pattern, cond, cond_len);
prefix = prepare_include_condition_pattern(&pattern);
+again:
if (prefix < 0)
goto done;
@@ -245,6 +247,20 @@ static int include_by_gitdir(const struct config_options *opts,
ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
icase ? WM_CASEFOLD : 0, NULL);
+ if (!ret && !already_tried_absolute) {
+ /*
+ * We've tried e.g. matching gitdir:~/work, but if
+ * ~/work is a symlink to /mnt/storage/work
+ * strbuf_realpath() will expand it, so the rule won't
+ * match. Let's match against a
+ * strbuf_add_absolute_path() version of the path,
+ * which'll do the right thing
+ */
+ strbuf_reset(&text);
+ strbuf_add_absolute_path(&text, git_dir);
+ already_tried_absolute = 1;
+ goto again;
+ }
done:
strbuf_release(&pattern);
strbuf_release(&text);
@@ -1965,7 +1981,7 @@ int git_config_get_expiry(const char *key, const char **output)
if (ret)
return ret;
if (strcmp(*output, "now")) {
- unsigned long now = approxidate("now");
+ timestamp_t now = approxidate("now");
if (approxidate(*output) >= now)
git_die_config(key, _("Invalid %s: '%s'"), key, *output);
}
diff --git a/configure.ac b/configure.ac
index 128165529f..deeb968daa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -250,8 +250,10 @@ AS_HELP_STRING([--with-openssl],[use OpenSSL library (default is YES)])
AS_HELP_STRING([], [ARG can be prefix for openssl library and headers]),
GIT_PARSE_WITH([openssl]))
-# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
-# able to use Perl-compatible regular expressions.
+# Define USE_LIBPCRE if you have and want to use libpcre. Various
+# commands such as log and grep offer runtime options to use
+# Perl-compatible regular expressions instead of standard or extended
+# POSIX regular expressions.
#
# Define LIBPCREDIR=/foo/bar if your libpcre header and library files are in
# /foo/bar/include and /foo/bar/lib directories.
@@ -499,8 +501,10 @@ GIT_CONF_SUBST([NEEDS_SSL_WITH_CRYPTO])
GIT_CONF_SUBST([NO_OPENSSL])
#
-# Define USE_LIBPCRE if you have and want to use libpcre. git-grep will be
-# able to use Perl-compatible regular expressions.
+# Define USE_LIBPCRE if you have and want to use libpcre. Various
+# commands such as log and grep offer runtime options to use
+# Perl-compatible regular expressions instead of standard or extended
+# POSIX regular expressions.
#
if test -n "$USE_LIBPCRE"; then
diff --git a/connect.c b/connect.c
index cd21a1b6f7..c72b1d1151 100644
--- a/connect.c
+++ b/connect.c
@@ -71,7 +71,7 @@ static void parse_one_symref_info(struct string_list *symref, const char *val, i
check_refname_format(target, REFNAME_ALLOW_ONELEVEL))
/* "symref=bogus:pair */
goto reject;
- item = string_list_append(symref, sym);
+ item = string_list_append_nodup(symref, sym);
item->util = target;
return;
reject:
diff --git a/contrib/completion/git-completion.bash b/contrib/completion/git-completion.bash
index 3f01249116..15b40f8774 100644
--- a/contrib/completion/git-completion.bash
+++ b/contrib/completion/git-completion.bash
@@ -1319,6 +1319,7 @@ _git_clone ()
--template=
--depth
--single-branch
+ --no-tags
--branch
--recurse-submodules
--no-single-branch
@@ -2387,13 +2388,18 @@ _git_config ()
color.status.added
color.status.changed
color.status.header
+ color.status.localBranch
color.status.nobranch
+ color.status.remoteBranch
color.status.unmerged
color.status.untracked
color.status.updated
color.ui
+ commit.cleanup
+ commit.gpgSign
commit.status
commit.template
+ commit.verbose
core.abbrev
core.askpass
core.attributesfile
diff --git a/convert.c b/convert.c
index 8d652bf27c..f1e168bc30 100644
--- a/convert.c
+++ b/convert.c
@@ -4,6 +4,7 @@
#include "quote.h"
#include "sigchain.h"
#include "pkt-line.h"
+#include "sub-process.h"
/*
* convert.c - convert a file when checking it out and checking it in.
@@ -497,126 +498,26 @@ static int apply_single_file_filter(const char *path, const char *src, size_t le
#define CAP_SMUDGE (1u<<1)
struct cmd2process {
- struct hashmap_entry ent; /* must be the first member! */
+ struct subprocess_entry subprocess; /* must be the first member! */
unsigned int supported_capabilities;
- const char *cmd;
- struct child_process process;
};
-static int cmd_process_map_initialized;
-static struct hashmap cmd_process_map;
-
-static int cmd2process_cmp(const struct cmd2process *e1,
- const struct cmd2process *e2,
- const void *unused)
-{
- return strcmp(e1->cmd, e2->cmd);
-}
-
-static struct cmd2process *find_multi_file_filter_entry(struct hashmap *hashmap, const char *cmd)
-{
- struct cmd2process key;
- hashmap_entry_init(&key, strhash(cmd));
- key.cmd = cmd;
- return hashmap_get(hashmap, &key, NULL);
-}
+static int subprocess_map_initialized;
+static struct hashmap subprocess_map;
-static int packet_write_list(int fd, const char *line, ...)
+static int start_multi_file_filter_fn(struct subprocess_entry *subprocess)
{
- va_list args;
int err;
- va_start(args, line);
- for (;;) {
- if (!line)
- break;
- if (strlen(line) > LARGE_PACKET_DATA_MAX)
- return -1;
- err = packet_write_fmt_gently(fd, "%s\n", line);
- if (err)
- return err;
- line = va_arg(args, const char*);
- }
- va_end(args);
- return packet_flush_gently(fd);
-}
-
-static void read_multi_file_filter_status(int fd, struct strbuf *status)
-{
- struct strbuf **pair;
- char *line;
- for (;;) {
- line = packet_read_line(fd, NULL);
- if (!line)
- break;
- pair = strbuf_split_str(line, '=', 2);
- if (pair[0] && pair[0]->len && pair[1]) {
- /* the last "status=<foo>" line wins */
- if (!strcmp(pair[0]->buf, "status=")) {
- strbuf_reset(status);
- strbuf_addbuf(status, pair[1]);
- }
- }
- strbuf_list_free(pair);
- }
-}
-
-static void kill_multi_file_filter(struct hashmap *hashmap, struct cmd2process *entry)
-{
- if (!entry)
- return;
-
- entry->process.clean_on_exit = 0;
- kill(entry->process.pid, SIGTERM);
- finish_command(&entry->process);
-
- hashmap_remove(hashmap, entry, NULL);
- free(entry);
-}
-
-static void stop_multi_file_filter(struct child_process *process)
-{
- sigchain_push(SIGPIPE, SIG_IGN);
- /* Closing the pipe signals the filter to initiate a shutdown. */
- close(process->in);
- close(process->out);
- sigchain_pop(SIGPIPE);
- /* Finish command will wait until the shutdown is complete. */
- finish_command(process);
-}
-
-static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, const char *cmd)
-{
- int err;
- struct cmd2process *entry;
- struct child_process *process;
- const char *argv[] = { cmd, NULL };
+ struct cmd2process *entry = (struct cmd2process *)subprocess;
struct string_list cap_list = STRING_LIST_INIT_NODUP;
char *cap_buf;
const char *cap_name;
-
- entry = xmalloc(sizeof(*entry));
- entry->cmd = cmd;
- entry->supported_capabilities = 0;
- process = &entry->process;
-
- child_process_init(process);
- process->argv = argv;
- process->use_shell = 1;
- process->in = -1;
- process->out = -1;
- process->clean_on_exit = 1;
- process->clean_on_exit_handler = stop_multi_file_filter;
-
- if (start_command(process)) {
- error("cannot fork to run external filter '%s'", cmd);
- return NULL;
- }
-
- hashmap_entry_init(entry, strhash(cmd));
+ struct child_process *process = &subprocess->process;
+ const char *cmd = subprocess->cmd;
sigchain_push(SIGPIPE, SIG_IGN);
- err = packet_write_list(process->in, "git-filter-client", "version=2", NULL);
+ err = packet_writel(process->in, "git-filter-client", "version=2", NULL);
if (err)
goto done;
@@ -632,7 +533,7 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons
if (err)
goto done;
- err = packet_write_list(process->in, "capability=clean", "capability=smudge", NULL);
+ err = packet_writel(process->in, "capability=clean", "capability=smudge", NULL);
for (;;) {
cap_buf = packet_read_line(process->out, NULL);
@@ -661,14 +562,7 @@ static struct cmd2process *start_multi_file_filter(struct hashmap *hashmap, cons
done:
sigchain_pop(SIGPIPE);
- if (err || errno == EPIPE) {
- error("initialization for external filter '%s' failed", cmd);
- kill_multi_file_filter(hashmap, entry);
- return NULL;
- }
-
- hashmap_add(hashmap, entry);
- return entry;
+ return err;
}
static int apply_multi_file_filter(const char *path, const char *src, size_t len,
@@ -682,22 +576,26 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
struct strbuf filter_status = STRBUF_INIT;
const char *filter_type;
- if (!cmd_process_map_initialized) {
- cmd_process_map_initialized = 1;
- hashmap_init(&cmd_process_map, (hashmap_cmp_fn) cmd2process_cmp, 0);
+ if (!subprocess_map_initialized) {
+ subprocess_map_initialized = 1;
+ hashmap_init(&subprocess_map, (hashmap_cmp_fn) cmd2process_cmp, 0);
entry = NULL;
} else {
- entry = find_multi_file_filter_entry(&cmd_process_map, cmd);
+ entry = (struct cmd2process *)subprocess_find_entry(&subprocess_map, cmd);
}
fflush(NULL);
if (!entry) {
- entry = start_multi_file_filter(&cmd_process_map, cmd);
- if (!entry)
+ entry = xmalloc(sizeof(*entry));
+ entry->supported_capabilities = 0;
+
+ if (subprocess_start(&subprocess_map, &entry->subprocess, cmd, start_multi_file_filter_fn)) {
+ free(entry);
return 0;
+ }
}
- process = &entry->process;
+ process = &entry->subprocess.process;
if (!(wanted_capability & entry->supported_capabilities))
return 0;
@@ -737,7 +635,10 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
if (err)
goto done;
- read_multi_file_filter_status(process->out, &filter_status);
+ err = subprocess_read_status(process->out, &filter_status);
+ if (err)
+ goto done;
+
err = strcmp(filter_status.buf, "success");
if (err)
goto done;
@@ -746,13 +647,16 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
if (err)
goto done;
- read_multi_file_filter_status(process->out, &filter_status);
+ err = subprocess_read_status(process->out, &filter_status);
+ if (err)
+ goto done;
+
err = strcmp(filter_status.buf, "success");
done:
sigchain_pop(SIGPIPE);
- if (err || errno == EPIPE) {
+ if (err) {
if (!strcmp(filter_status.buf, "error")) {
/* The filter signaled a problem with the file. */
} else if (!strcmp(filter_status.buf, "abort")) {
@@ -768,7 +672,8 @@ done:
* Force shutdown and restart if another blob requires filtering.
*/
error("external filter '%s' failed", cmd);
- kill_multi_file_filter(&cmd_process_map, entry);
+ subprocess_stop(&subprocess_map, &entry->subprocess);
+ free(entry);
}
} else {
strbuf_swap(dst, &nbuf);
diff --git a/credential-cache--daemon.c b/credential-cache--daemon.c
index 46c5937526..f3814cc47a 100644
--- a/credential-cache--daemon.c
+++ b/credential-cache--daemon.c
@@ -8,7 +8,7 @@ static struct tempfile socket_file;
struct credential_cache_entry {
struct credential item;
- unsigned long expiration;
+ timestamp_t expiration;
};
static struct credential_cache_entry *entries;
static int entries_nr;
@@ -47,12 +47,12 @@ static void remove_credential(const struct credential *c)
e->expiration = 0;
}
-static int check_expirations(void)
+static timestamp_t check_expirations(void)
{
- static unsigned long wait_for_entry_until;
+ static timestamp_t wait_for_entry_until;
int i = 0;
- unsigned long now = time(NULL);
- unsigned long next = (unsigned long)-1;
+ timestamp_t now = time(NULL);
+ timestamp_t next = TIME_MAX;
/*
* Initially give the client 30 seconds to actually contact us
@@ -159,7 +159,7 @@ static void serve_one_client(FILE *in, FILE *out)
static int serve_cache_loop(int fd)
{
struct pollfd pfd;
- unsigned long wakeup;
+ timestamp_t wakeup;
wakeup = check_expirations();
if (!wakeup)
diff --git a/date.c b/date.c
index a996331f5b..63fa99685e 100644
--- a/date.c
+++ b/date.c
@@ -39,14 +39,24 @@ static const char *weekday_names[] = {
"Sundays", "Mondays", "Tuesdays", "Wednesdays", "Thursdays", "Fridays", "Saturdays"
};
-static time_t gm_time_t(unsigned long time, int tz)
+static time_t gm_time_t(timestamp_t time, int tz)
{
int minutes;
minutes = tz < 0 ? -tz : tz;
minutes = (minutes / 100)*60 + (minutes % 100);
minutes = tz < 0 ? -minutes : minutes;
- return time + minutes * 60;
+
+ if (minutes > 0) {
+ if (unsigned_add_overflows(time, minutes * 60))
+ die("Timestamp+tz too large: %"PRItime" +%04d",
+ time, tz);
+ } else if (time < -minutes * 60)
+ die("Timestamp before Unix epoch: %"PRItime" %04d", time, tz);
+ time += minutes * 60;
+ if (date_overflows(time))
+ die("Timestamp too large for this system: %"PRItime, time);
+ return (time_t)time;
}
/*
@@ -54,7 +64,7 @@ static time_t gm_time_t(unsigned long time, int tz)
* thing, which means that tz -0100 is passed in as the integer -100,
* even though it means "sixty minutes off"
*/
-static struct tm *time_to_tm(unsigned long time, int tz)
+static struct tm *time_to_tm(timestamp_t time, int tz)
{
time_t t = gm_time_t(time, tz);
return gmtime(&t);
@@ -64,13 +74,16 @@ static struct tm *time_to_tm(unsigned long time, int tz)
* What value of "tz" was in effect back then at "time" in the
* local timezone?
*/
-static int local_tzoffset(unsigned long time)
+static int local_tzoffset(timestamp_t time)
{
time_t t, t_local;
struct tm tm;
int offset, eastwest;
- t = time;
+ if (date_overflows(time))
+ die("Timestamp too large for this system: %"PRItime, time);
+
+ t = (time_t)time;
localtime_r(&t, &tm);
t_local = tm_to_time_t(&tm);
@@ -88,11 +101,11 @@ static int local_tzoffset(unsigned long time)
return offset * eastwest;
}
-void show_date_relative(unsigned long time, int tz,
+void show_date_relative(timestamp_t time, int tz,
const struct timeval *now,
struct strbuf *timebuf)
{
- unsigned long diff;
+ timestamp_t diff;
if (now->tv_sec < time) {
strbuf_addstr(timebuf, _("in the future"));
return;
@@ -100,65 +113,65 @@ void show_date_relative(unsigned long time, int tz,
diff = now->tv_sec - time;
if (diff < 90) {
strbuf_addf(timebuf,
- Q_("%lu second ago", "%lu seconds ago", diff), diff);
+ Q_("%"PRItime" second ago", "%"PRItime" seconds ago", diff), diff);
return;
}
/* Turn it into minutes */
diff = (diff + 30) / 60;
if (diff < 90) {
strbuf_addf(timebuf,
- Q_("%lu minute ago", "%lu minutes ago", diff), diff);
+ Q_("%"PRItime" minute ago", "%"PRItime" minutes ago", diff), diff);
return;
}
/* Turn it into hours */
diff = (diff + 30) / 60;
if (diff < 36) {
strbuf_addf(timebuf,
- Q_("%lu hour ago", "%lu hours ago", diff), diff);
+ Q_("%"PRItime" hour ago", "%"PRItime" hours ago", diff), diff);
return;
}
/* We deal with number of days from here on */
diff = (diff + 12) / 24;
if (diff < 14) {
strbuf_addf(timebuf,
- Q_("%lu day ago", "%lu days ago", diff), diff);
+ Q_("%"PRItime" day ago", "%"PRItime" days ago", diff), diff);
return;
}
/* Say weeks for the past 10 weeks or so */
if (diff < 70) {
strbuf_addf(timebuf,
- Q_("%lu week ago", "%lu weeks ago", (diff + 3) / 7),
+ Q_("%"PRItime" week ago", "%"PRItime" weeks ago", (diff + 3) / 7),
(diff + 3) / 7);
return;
}
/* Say months for the past 12 months or so */
if (diff < 365) {
strbuf_addf(timebuf,
- Q_("%lu month ago", "%lu months ago", (diff + 15) / 30),
+ Q_("%"PRItime" month ago", "%"PRItime" months ago", (diff + 15) / 30),
(diff + 15) / 30);
return;
}
/* Give years and months for 5 years or so */
if (diff < 1825) {
- unsigned long totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
- unsigned long years = totalmonths / 12;
- unsigned long months = totalmonths % 12;
+ timestamp_t totalmonths = (diff * 12 * 2 + 365) / (365 * 2);
+ timestamp_t years = totalmonths / 12;
+ timestamp_t months = totalmonths % 12;
if (months) {
struct strbuf sb = STRBUF_INIT;
- strbuf_addf(&sb, Q_("%lu year", "%lu years", years), years);
+ strbuf_addf(&sb, Q_("%"PRItime" year", "%"PRItime" years", years), years);
strbuf_addf(timebuf,
/* TRANSLATORS: "%s" is "<n> years" */
- Q_("%s, %lu month ago", "%s, %lu months ago", months),
+ Q_("%s, %"PRItime" month ago", "%s, %"PRItime" months ago", months),
sb.buf, months);
strbuf_release(&sb);
} else
strbuf_addf(timebuf,
- Q_("%lu year ago", "%lu years ago", years), years);
+ Q_("%"PRItime" year ago", "%"PRItime" years ago", years), years);
return;
}
/* Otherwise, just years. Centuries is probably overkill. */
strbuf_addf(timebuf,
- Q_("%lu year ago", "%lu years ago", (diff + 183) / 365),
+ Q_("%"PRItime" year ago", "%"PRItime" years ago", (diff + 183) / 365),
(diff + 183) / 365);
}
@@ -172,14 +185,14 @@ struct date_mode *date_mode_from_type(enum date_mode_type type)
return &mode;
}
-const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
+const char *show_date(timestamp_t time, int tz, const struct date_mode *mode)
{
struct tm *tm;
static struct strbuf timebuf = STRBUF_INIT;
if (mode->type == DATE_UNIX) {
strbuf_reset(&timebuf);
- strbuf_addf(&timebuf, "%lu", time);
+ strbuf_addf(&timebuf, "%"PRItime, time);
return timebuf.buf;
}
@@ -188,7 +201,7 @@ const char *show_date(unsigned long time, int tz, const struct date_mode *mode)
if (mode->type == DATE_RAW) {
strbuf_reset(&timebuf);
- strbuf_addf(&timebuf, "%lu %+05d", time, tz);
+ strbuf_addf(&timebuf, "%"PRItime" %+05d", time, tz);
return timebuf.buf;
}
@@ -425,7 +438,7 @@ static int is_date(int year, int month, int day, struct tm *now_tm, time_t now,
return 0;
}
-static int match_multi_number(unsigned long num, char c, const char *date,
+static int match_multi_number(timestamp_t num, char c, const char *date,
char *end, struct tm *tm, time_t now)
{
struct tm now_tm;
@@ -508,9 +521,9 @@ static int match_digit(const char *date, struct tm *tm, int *offset, int *tm_gmt
{
int n;
char *end;
- unsigned long num;
+ timestamp_t num;
- num = strtoul(date, &end, 10);
+ num = parse_timestamp(date, &end, 10);
/*
* Seconds since 1970? We trigger on that for any numbers with
@@ -635,7 +648,7 @@ static int match_tz(const char *date, int *offp)
return end - date;
}
-static void date_string(unsigned long date, int offset, struct strbuf *buf)
+static void date_string(timestamp_t date, int offset, struct strbuf *buf)
{
int sign = '+';
@@ -643,23 +656,23 @@ static void date_string(unsigned long date, int offset, struct strbuf *buf)
offset = -offset;
sign = '-';
}
- strbuf_addf(buf, "%lu %c%02d%02d", date, sign, offset / 60, offset % 60);
+ strbuf_addf(buf, "%"PRItime" %c%02d%02d", date, sign, offset / 60, offset % 60);
}
/*
* Parse a string like "0 +0000" as ancient timestamp near epoch, but
* only when it appears not as part of any other string.
*/
-static int match_object_header_date(const char *date, unsigned long *timestamp, int *offset)
+static int match_object_header_date(const char *date, timestamp_t *timestamp, int *offset)
{
char *end;
- unsigned long stamp;
+ timestamp_t stamp;
int ofs;
if (*date < '0' || '9' < *date)
return -1;
- stamp = strtoul(date, &end, 10);
- if (*end != ' ' || stamp == ULONG_MAX || (end[1] != '+' && end[1] != '-'))
+ stamp = parse_timestamp(date, &end, 10);
+ if (*end != ' ' || stamp == TIME_MAX || (end[1] != '+' && end[1] != '-'))
return -1;
date = end + 2;
ofs = strtol(date, &end, 10);
@@ -675,11 +688,11 @@ static int match_object_header_date(const char *date, unsigned long *timestamp,
/* Gr. strptime is crap for this; it doesn't have a way to require RFC2822
(i.e. English) day/month names, and it doesn't work correctly with %z. */
-int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
+int parse_date_basic(const char *date, timestamp_t *timestamp, int *offset)
{
struct tm tm;
int tm_gmt;
- unsigned long dummy_timestamp;
+ timestamp_t dummy_timestamp;
int dummy_offset;
if (!timestamp)
@@ -747,7 +760,7 @@ int parse_date_basic(const char *date, unsigned long *timestamp, int *offset)
return 0; /* success */
}
-int parse_expiry_date(const char *date, unsigned long *timestamp)
+int parse_expiry_date(const char *date, timestamp_t *timestamp)
{
int errors = 0;
@@ -762,7 +775,7 @@ int parse_expiry_date(const char *date, unsigned long *timestamp)
* of the past, and there is nothing from the future
* to be kept.
*/
- *timestamp = ULONG_MAX;
+ *timestamp = TIME_MAX;
else
*timestamp = approxidate_careful(date, &errors);
@@ -771,7 +784,7 @@ int parse_expiry_date(const char *date, unsigned long *timestamp)
int parse_date(const char *date, struct strbuf *result)
{
- unsigned long timestamp;
+ timestamp_t timestamp;
int offset;
if (parse_date_basic(date, &timestamp, &offset))
return -1;
@@ -845,7 +858,7 @@ void datestamp(struct strbuf *out)
* Relative time update (eg "2 days ago"). If we haven't set the time
* yet, we need to set it from current time.
*/
-static unsigned long update_tm(struct tm *tm, struct tm *now, unsigned long sec)
+static time_t update_tm(struct tm *tm, struct tm *now, time_t sec)
{
time_t n;
@@ -1066,7 +1079,7 @@ static const char *approxidate_digit(const char *date, struct tm *tm, int *num,
time_t now)
{
char *end;
- unsigned long number = strtoul(date, &end, 10);
+ timestamp_t number = parse_timestamp(date, &end, 10);
switch (*end) {
case ':':
@@ -1114,9 +1127,9 @@ static void pending_number(struct tm *tm, int *num)
}
}
-static unsigned long approxidate_str(const char *date,
- const struct timeval *tv,
- int *error_ret)
+static timestamp_t approxidate_str(const char *date,
+ const struct timeval *tv,
+ int *error_ret)
{
int number = 0;
int touched = 0;
@@ -1148,12 +1161,12 @@ static unsigned long approxidate_str(const char *date,
pending_number(&tm, &number);
if (!touched)
*error_ret = 1;
- return update_tm(&tm, &now, 0);
+ return (timestamp_t)update_tm(&tm, &now, 0);
}
-unsigned long approxidate_relative(const char *date, const struct timeval *tv)
+timestamp_t approxidate_relative(const char *date, const struct timeval *tv)
{
- unsigned long timestamp;
+ timestamp_t timestamp;
int offset;
int errors = 0;
@@ -1162,10 +1175,10 @@ unsigned long approxidate_relative(const char *date, const struct timeval *tv)
return approxidate_str(date, tv, &errors);
}
-unsigned long approxidate_careful(const char *date, int *error_ret)
+timestamp_t approxidate_careful(const char *date, int *error_ret)
{
struct timeval tv;
- unsigned long timestamp;
+ timestamp_t timestamp;
int offset;
int dummy = 0;
if (!error_ret)
@@ -1180,12 +1193,12 @@ unsigned long approxidate_careful(const char *date, int *error_ret)
return approxidate_str(date, &tv, error_ret);
}
-int date_overflows(unsigned long t)
+int date_overflows(timestamp_t t)
{
time_t sys;
- /* If we overflowed our unsigned long, that's bad... */
- if (t == ULONG_MAX)
+ /* If we overflowed our timestamp data type, that's bad... */
+ if ((uintmax_t)t >= TIME_MAX)
return 1;
/*
diff --git a/diff-lib.c b/diff-lib.c
index 52447466b5..2982bf055a 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -478,7 +478,7 @@ static int oneway_diff(const struct cache_entry * const *src,
}
static int diff_cache(struct rev_info *revs,
- const unsigned char *tree_sha1,
+ const struct object_id *tree_oid,
const char *tree_name,
int cached)
{
@@ -486,10 +486,10 @@ static int diff_cache(struct rev_info *revs,
struct tree_desc t;
struct unpack_trees_options opts;
- tree = parse_tree_indirect(tree_sha1);
+ tree = parse_tree_indirect(tree_oid);
if (!tree)
return error("bad tree object %s",
- tree_name ? tree_name : sha1_to_hex(tree_sha1));
+ tree_name ? tree_name : oid_to_hex(tree_oid));
memset(&opts, 0, sizeof(opts));
opts.head_idx = 1;
opts.index_only = cached;
@@ -512,7 +512,7 @@ int run_diff_index(struct rev_info *revs, int cached)
struct object_array_entry *ent;
ent = revs->pending.objects;
- if (diff_cache(revs, ent->item->oid.hash, ent->name, cached))
+ if (diff_cache(revs, &ent->item->oid, ent->name, cached))
exit(128);
diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
@@ -522,7 +522,7 @@ int run_diff_index(struct rev_info *revs, int cached)
return 0;
}
-int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
+int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
{
struct rev_info revs;
@@ -530,7 +530,7 @@ int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt)
copy_pathspec(&revs.prune_data, &opt->pathspec);
revs.diffopt = *opt;
- if (diff_cache(&revs, tree_sha1, NULL, 1))
+ if (diff_cache(&revs, tree_oid, NULL, 1))
exit(128);
return 0;
}
diff --git a/diff.c b/diff.c
index 74283d9001..5275c4b780 100644
--- a/diff.c
+++ b/diff.c
@@ -27,7 +27,7 @@
#endif
static int diff_detect_rename_default;
-static int diff_indent_heuristic; /* experimental */
+static int diff_indent_heuristic = 1;
static int diff_rename_limit_default = 400;
static int diff_suppress_blank_empty;
static int diff_use_color_default = -1;
@@ -290,9 +290,6 @@ int git_diff_ui_config(const char *var, const char *value, void *cb)
return 0;
}
- if (git_diff_heuristic_config(var, value, cb) < 0)
- return -1;
-
if (!strcmp(var, "diff.wserrorhighlight")) {
int val = parse_ws_error_highlight(value);
if (val < 0)
@@ -351,6 +348,9 @@ int git_diff_basic_config(const char *var, const char *value, void *cb)
if (starts_with(var, "submodule."))
return parse_submodule_config_option(var, value);
+ if (git_diff_heuristic_config(var, value, cb) < 0)
+ return -1;
+
return git_default_config(var, value, cb);
}
@@ -5244,7 +5244,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
if (driver->textconv_cache && df->oid_valid) {
*outbuf = notes_cache_get(driver->textconv_cache,
- df->oid.hash,
+ &df->oid,
&size);
if (*outbuf)
return size;
@@ -5256,7 +5256,7 @@ size_t fill_textconv(struct userdiff_driver *driver,
if (driver->textconv_cache && df->oid_valid) {
/* ignore errors, as we might be in a readonly repository */
- notes_cache_put(driver->textconv_cache, df->oid.hash, *outbuf,
+ notes_cache_put(driver->textconv_cache, &df->oid, *outbuf,
size);
/*
* we could save up changes and flush them all at the end,
@@ -5270,6 +5270,29 @@ size_t fill_textconv(struct userdiff_driver *driver,
return size;
}
+int textconv_object(const char *path,
+ unsigned mode,
+ const struct object_id *oid,
+ int oid_valid,
+ char **buf,
+ unsigned long *buf_size)
+{
+ struct diff_filespec *df;
+ struct userdiff_driver *textconv;
+
+ df = alloc_filespec(path);
+ fill_filespec(df, oid->hash, oid_valid, mode);
+ textconv = get_textconv(df);
+ if (!textconv) {
+ free_filespec(df);
+ return 0;
+ }
+
+ *buf_size = fill_textconv(textconv, df, buf);
+ free_filespec(df);
+ return 1;
+}
+
void setup_diff_pager(struct diff_options *opt)
{
/*
diff --git a/diff.h b/diff.h
index 5be1ee77a7..67537f17ed 100644
--- a/diff.h
+++ b/diff.h
@@ -354,7 +354,7 @@ extern const char *diff_aligned_abbrev(const struct object_id *sha1, int);
extern int run_diff_files(struct rev_info *revs, unsigned int option);
extern int run_diff_index(struct rev_info *revs, int cached);
-extern int do_diff_cache(const unsigned char *, struct diff_options *);
+extern int do_diff_cache(const struct object_id *, struct diff_options *);
extern int diff_flush_patch_id(struct diff_options *, unsigned char *, int);
extern int diff_result_code(struct diff_options *, int);
@@ -385,6 +385,13 @@ extern size_t fill_textconv(struct userdiff_driver *driver,
*/
extern struct userdiff_driver *get_textconv(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.
+ */
+extern int textconv_object(const char *path, unsigned mode, const struct object_id *oid, int oid_valid, char **buf, unsigned long *buf_size);
+
extern int parse_rename_score(const char **cp_p);
extern long parse_algorithm_value(const char *value);
diff --git a/dir.c b/dir.c
index f451bfa48c..9efcf1eab6 100644
--- a/dir.c
+++ b/dir.c
@@ -7,6 +7,7 @@
* Copyright (C) Linus Torvalds, 2005-2006
* Junio Hamano, 2005-2006
*/
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
#include "dir.h"
#include "attr.h"
@@ -45,9 +46,11 @@ struct cached_dir {
};
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
- const char *path, int len, struct untracked_cache_dir *untracked,
+ struct index_state *istate, const char *path, int len,
+ struct untracked_cache_dir *untracked,
int check_only, const struct pathspec *pathspec);
-static int get_dtype(struct dirent *de, const char *path, int len);
+static int get_dtype(struct dirent *de, struct index_state *istate,
+ const char *path, int len);
int fspathcmp(const char *a, const char *b)
{
@@ -174,7 +177,9 @@ char *common_prefix(const struct pathspec *pathspec)
return len ? xmemdupz(pathspec->items[0].match, len) : NULL;
}
-int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
+int fill_directory(struct dir_struct *dir,
+ struct index_state *istate,
+ const struct pathspec *pathspec)
{
const char *prefix;
size_t prefix_len;
@@ -187,7 +192,7 @@ int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec)
prefix = prefix_len ? pathspec->items[0].match : "";
/* Read the directory and prune it */
- read_directory(dir, prefix, prefix_len, pathspec);
+ read_directory(dir, istate, prefix, prefix_len, pathspec);
return prefix_len;
}
@@ -587,7 +592,8 @@ void add_exclude(const char *string, const char *base,
x->el = el;
}
-static void *read_skip_worktree_file_from_index(const char *path, size_t *size,
+static void *read_skip_worktree_file_from_index(const struct index_state *istate,
+ const char *path, size_t *size,
struct sha1_stat *sha1_stat)
{
int pos, len;
@@ -596,12 +602,12 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size,
void *data;
len = strlen(path);
- pos = cache_name_pos(path, len);
+ pos = index_name_pos(istate, path, len);
if (pos < 0)
return NULL;
- if (!ce_skip_worktree(active_cache[pos]))
+ if (!ce_skip_worktree(istate->cache[pos]))
return NULL;
- data = read_sha1_file(active_cache[pos]->oid.hash, &type, &sz);
+ data = read_sha1_file(istate->cache[pos]->oid.hash, &type, &sz);
if (!data || type != OBJ_BLOB) {
free(data);
return NULL;
@@ -609,7 +615,7 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size,
*size = xsize_t(sz);
if (sha1_stat) {
memset(&sha1_stat->stat, 0, sizeof(sha1_stat->stat));
- hashcpy(sha1_stat->sha1, active_cache[pos]->oid.hash);
+ hashcpy(sha1_stat->sha1, istate->cache[pos]->oid.hash);
}
return data;
}
@@ -727,7 +733,7 @@ static void invalidate_directory(struct untracked_cache *uc,
/*
* Given a file with name "fname", read it (either from disk, or from
- * the index if "check_index" is non-zero), parse it and store the
+ * an index if 'istate' is non-null), parse it and store the
* exclude rules in "el".
*
* If "ss" is not NULL, compute SHA-1 of the exclude file and fill
@@ -735,7 +741,8 @@ static void invalidate_directory(struct untracked_cache *uc,
* ss_valid is non-zero, "ss" must contain good value as input.
*/
static int add_excludes(const char *fname, const char *base, int baselen,
- struct exclude_list *el, int check_index,
+ struct exclude_list *el,
+ struct index_state *istate,
struct sha1_stat *sha1_stat)
{
struct stat st;
@@ -749,8 +756,8 @@ static int add_excludes(const char *fname, const char *base, int baselen,
warn_on_inaccessible(fname);
if (0 <= fd)
close(fd);
- if (!check_index ||
- (buf = read_skip_worktree_file_from_index(fname, &size, sha1_stat)) == NULL)
+ if (!istate ||
+ (buf = read_skip_worktree_file_from_index(istate, fname, &size, sha1_stat)) == NULL)
return -1;
if (size == 0) {
free(buf);
@@ -782,15 +789,15 @@ static int add_excludes(const char *fname, const char *base, int baselen,
if (sha1_stat) {
int pos;
if (sha1_stat->valid &&
- !match_stat_data_racy(&the_index, &sha1_stat->stat, &st))
+ !match_stat_data_racy(istate, &sha1_stat->stat, &st))
; /* no content change, ss->sha1 still good */
- else if (check_index &&
- (pos = cache_name_pos(fname, strlen(fname))) >= 0 &&
- !ce_stage(active_cache[pos]) &&
- ce_uptodate(active_cache[pos]) &&
+ else if (istate &&
+ (pos = index_name_pos(istate, fname, strlen(fname))) >= 0 &&
+ !ce_stage(istate->cache[pos]) &&
+ ce_uptodate(istate->cache[pos]) &&
!would_convert_to_git(fname))
hashcpy(sha1_stat->sha1,
- active_cache[pos]->oid.hash);
+ istate->cache[pos]->oid.hash);
else
hash_sha1_file(buf, size, "blob", sha1_stat->sha1);
fill_stat_data(&sha1_stat->stat, &st);
@@ -821,9 +828,9 @@ static int add_excludes(const char *fname, const char *base, int baselen,
int add_excludes_from_file_to_list(const char *fname, const char *base,
int baselen, struct exclude_list *el,
- int check_index)
+ struct index_state *istate)
{
- return add_excludes(fname, base, baselen, el, check_index, NULL);
+ return add_excludes(fname, base, baselen, el, istate, NULL);
}
struct exclude_list *add_exclude_list(struct dir_struct *dir,
@@ -855,7 +862,7 @@ static void add_excludes_from_file_1(struct dir_struct *dir, const char *fname,
if (!dir->untracked)
dir->unmanaged_exclude_files++;
el = add_exclude_list(dir, EXC_FILE, fname);
- if (add_excludes(fname, "", 0, el, 0, sha1_stat) < 0)
+ if (add_excludes(fname, "", 0, el, NULL, sha1_stat) < 0)
die("cannot use %s as an exclude file", fname);
}
@@ -958,7 +965,8 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname,
int pathlen,
const char *basename,
int *dtype,
- struct exclude_list *el)
+ struct exclude_list *el,
+ struct index_state *istate)
{
struct exclude *exc = NULL; /* undecided */
int i;
@@ -973,7 +981,7 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname,
if (x->flags & EXC_FLAG_MUSTBEDIR) {
if (*dtype == DT_UNKNOWN)
- *dtype = get_dtype(NULL, pathname, pathlen);
+ *dtype = get_dtype(NULL, istate, pathname, pathlen);
if (*dtype != DT_DIR)
continue;
}
@@ -1006,16 +1014,18 @@ static struct exclude *last_exclude_matching_from_list(const char *pathname,
*/
int is_excluded_from_list(const char *pathname,
int pathlen, const char *basename, int *dtype,
- struct exclude_list *el)
+ struct exclude_list *el, struct index_state *istate)
{
struct exclude *exclude;
- exclude = last_exclude_matching_from_list(pathname, pathlen, basename, dtype, el);
+ exclude = last_exclude_matching_from_list(pathname, pathlen, basename,
+ dtype, el, istate);
if (exclude)
return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
return -1; /* undecided */
}
static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir,
+ struct index_state *istate,
const char *pathname, int pathlen, const char *basename,
int *dtype_p)
{
@@ -1027,7 +1037,7 @@ static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir,
for (j = group->nr - 1; j >= 0; j--) {
exclude = last_exclude_matching_from_list(
pathname, pathlen, basename, dtype_p,
- &group->el[j]);
+ &group->el[j], istate);
if (exclude)
return exclude;
}
@@ -1039,7 +1049,9 @@ static struct exclude *last_exclude_matching_from_lists(struct dir_struct *dir,
* Loads the per-directory exclude list for the substring of base
* which has a char length of baselen.
*/
-static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
+static void prep_exclude(struct dir_struct *dir,
+ struct index_state *istate,
+ const char *base, int baselen)
{
struct exclude_list_group *group;
struct exclude_list *el;
@@ -1118,6 +1130,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
int dt = DT_DIR;
dir->basebuf.buf[stk->baselen - 1] = 0;
dir->exclude = last_exclude_matching_from_lists(dir,
+ istate,
dir->basebuf.buf, stk->baselen - 1,
dir->basebuf.buf + current, &dt);
dir->basebuf.buf[stk->baselen - 1] = '/';
@@ -1159,7 +1172,7 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
strbuf_addbuf(&sb, &dir->basebuf);
strbuf_addstr(&sb, dir->exclude_per_dir);
el->src = strbuf_detach(&sb, NULL);
- add_excludes(el->src, el->src, stk->baselen, el, 1,
+ add_excludes(el->src, el->src, stk->baselen, el, istate,
untracked ? &sha1_stat : NULL);
}
/*
@@ -1194,19 +1207,20 @@ static void prep_exclude(struct dir_struct *dir, const char *base, int baselen)
* undecided.
*/
struct exclude *last_exclude_matching(struct dir_struct *dir,
- const char *pathname,
- int *dtype_p)
+ struct index_state *istate,
+ const char *pathname,
+ int *dtype_p)
{
int pathlen = strlen(pathname);
const char *basename = strrchr(pathname, '/');
basename = (basename) ? basename+1 : pathname;
- prep_exclude(dir, pathname, basename-pathname);
+ prep_exclude(dir, istate, pathname, basename-pathname);
if (dir->exclude)
return dir->exclude;
- return last_exclude_matching_from_lists(dir, pathname, pathlen,
+ return last_exclude_matching_from_lists(dir, istate, pathname, pathlen,
basename, dtype_p);
}
@@ -1215,10 +1229,11 @@ struct exclude *last_exclude_matching(struct dir_struct *dir,
* scans all exclude lists to determine whether pathname is excluded.
* Returns 1 if true, otherwise 0.
*/
-int is_excluded(struct dir_struct *dir, const char *pathname, int *dtype_p)
+int is_excluded(struct dir_struct *dir, struct index_state *istate,
+ const char *pathname, int *dtype_p)
{
struct exclude *exclude =
- last_exclude_matching(dir, pathname, dtype_p);
+ last_exclude_matching(dir, istate, pathname, dtype_p);
if (exclude)
return exclude->flags & EXC_FLAG_NEGATIVE ? 0 : 1;
return 0;
@@ -1233,18 +1248,22 @@ static struct dir_entry *dir_entry_new(const char *pathname, int len)
return ent;
}
-static struct dir_entry *dir_add_name(struct dir_struct *dir, const char *pathname, int len)
+static struct dir_entry *dir_add_name(struct dir_struct *dir,
+ struct index_state *istate,
+ const char *pathname, int len)
{
- if (cache_file_exists(pathname, len, ignore_case))
+ if (index_file_exists(istate, pathname, len, ignore_case))
return NULL;
ALLOC_GROW(dir->entries, dir->nr+1, dir->alloc);
return dir->entries[dir->nr++] = dir_entry_new(pathname, len);
}
-struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len)
+struct dir_entry *dir_add_ignored(struct dir_struct *dir,
+ struct index_state *istate,
+ const char *pathname, int len)
{
- if (!cache_name_is_other(pathname, len))
+ if (!index_name_is_other(istate, pathname, len))
return NULL;
ALLOC_GROW(dir->ignored, dir->ignored_nr+1, dir->ignored_alloc);
@@ -1262,14 +1281,15 @@ enum exist_status {
* the directory name; instead, use the case insensitive
* directory hash.
*/
-static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
+static enum exist_status directory_exists_in_index_icase(struct index_state *istate,
+ const char *dirname, int len)
{
struct cache_entry *ce;
- if (cache_dir_exists(dirname, len))
+ if (index_dir_exists(istate, dirname, len))
return index_directory;
- ce = cache_file_exists(dirname, len, ignore_case);
+ ce = index_file_exists(istate, dirname, len, ignore_case);
if (ce && S_ISGITLINK(ce->ce_mode))
return index_gitdir;
@@ -1283,18 +1303,19 @@ static enum exist_status directory_exists_in_index_icase(const char *dirname, in
* the files it contains) will sort with the '/' at the
* end.
*/
-static enum exist_status directory_exists_in_index(const char *dirname, int len)
+static enum exist_status directory_exists_in_index(struct index_state *istate,
+ const char *dirname, int len)
{
int pos;
if (ignore_case)
- return directory_exists_in_index_icase(dirname, len);
+ return directory_exists_in_index_icase(istate, dirname, len);
- pos = cache_name_pos(dirname, len);
+ pos = index_name_pos(istate, dirname, len);
if (pos < 0)
pos = -pos-1;
- while (pos < active_nr) {
- const struct cache_entry *ce = active_cache[pos++];
+ while (pos < istate->cache_nr) {
+ const struct cache_entry *ce = istate->cache[pos++];
unsigned char endchar;
if (strncmp(ce->name, dirname, len))
@@ -1344,12 +1365,13 @@ static enum exist_status directory_exists_in_index(const char *dirname, int len)
* (c) otherwise, we recurse into it.
*/
static enum path_treatment treat_directory(struct dir_struct *dir,
+ struct index_state *istate,
struct untracked_cache_dir *untracked,
const char *dirname, int len, int baselen, int exclude,
const struct pathspec *pathspec)
{
/* The "len-1" is to strip the final '/' */
- switch (directory_exists_in_index(dirname, len-1)) {
+ switch (directory_exists_in_index(istate, dirname, len-1)) {
case index_directory:
return path_recurse;
@@ -1374,7 +1396,7 @@ static enum path_treatment treat_directory(struct dir_struct *dir,
untracked = lookup_untracked(dir->untracked, untracked,
dirname + baselen, len - baselen);
- return read_directory_recursive(dir, dirname, len,
+ return read_directory_recursive(dir, istate, dirname, len,
untracked, 1, pathspec);
}
@@ -1455,12 +1477,13 @@ static int exclude_matches_pathspec(const char *path, int pathlen,
return 0;
}
-static int get_index_dtype(const char *path, int len)
+static int get_index_dtype(struct index_state *istate,
+ const char *path, int len)
{
int pos;
const struct cache_entry *ce;
- ce = cache_file_exists(path, len, 0);
+ ce = index_file_exists(istate, path, len, 0);
if (ce) {
if (!ce_uptodate(ce))
return DT_UNKNOWN;
@@ -1474,12 +1497,12 @@ static int get_index_dtype(const char *path, int len)
}
/* Try to look it up as a directory */
- pos = cache_name_pos(path, len);
+ pos = index_name_pos(istate, path, len);
if (pos >= 0)
return DT_UNKNOWN;
pos = -pos-1;
- while (pos < active_nr) {
- ce = active_cache[pos++];
+ while (pos < istate->cache_nr) {
+ ce = istate->cache[pos++];
if (strncmp(ce->name, path, len))
break;
if (ce->name[len] > '/')
@@ -1493,14 +1516,15 @@ static int get_index_dtype(const char *path, int len)
return DT_UNKNOWN;
}
-static int get_dtype(struct dirent *de, const char *path, int len)
+static int get_dtype(struct dirent *de, struct index_state *istate,
+ const char *path, int len)
{
int dtype = de ? DTYPE(de) : DT_UNKNOWN;
struct stat st;
if (dtype != DT_UNKNOWN)
return dtype;
- dtype = get_index_dtype(path, len);
+ dtype = get_index_dtype(istate, path, len);
if (dtype != DT_UNKNOWN)
return dtype;
if (lstat(path, &st))
@@ -1516,16 +1540,17 @@ static int get_dtype(struct dirent *de, const char *path, int len)
static enum path_treatment treat_one_path(struct dir_struct *dir,
struct untracked_cache_dir *untracked,
+ struct index_state *istate,
struct strbuf *path,
int baselen,
const struct pathspec *pathspec,
int dtype, struct dirent *de)
{
int exclude;
- int has_path_in_index = !!cache_file_exists(path->buf, path->len, ignore_case);
+ int has_path_in_index = !!index_file_exists(istate, path->buf, path->len, ignore_case);
if (dtype == DT_UNKNOWN)
- dtype = get_dtype(de, path->buf, path->len);
+ dtype = get_dtype(de, istate, path->buf, path->len);
/* Always exclude indexed files */
if (dtype != DT_DIR && has_path_in_index)
@@ -1552,10 +1577,10 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
if ((dir->flags & DIR_COLLECT_KILLED_ONLY) &&
(dtype == DT_DIR) &&
!has_path_in_index &&
- (directory_exists_in_index(path->buf, path->len) == index_nonexistent))
+ (directory_exists_in_index(istate, path->buf, path->len) == index_nonexistent))
return path_none;
- exclude = is_excluded(dir, path->buf, &dtype);
+ exclude = is_excluded(dir, istate, path->buf, &dtype);
/*
* Excluded? If we don't explicitly want to show
@@ -1569,7 +1594,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
return path_none;
case DT_DIR:
strbuf_addch(path, '/');
- return treat_directory(dir, untracked, path->buf, path->len,
+ return treat_directory(dir, istate, untracked, path->buf, path->len,
baselen, exclude, pathspec);
case DT_REG:
case DT_LNK:
@@ -1580,6 +1605,7 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
static enum path_treatment treat_path_fast(struct dir_struct *dir,
struct untracked_cache_dir *untracked,
struct cached_dir *cdir,
+ struct index_state *istate,
struct strbuf *path,
int baselen,
const struct pathspec *pathspec)
@@ -1598,7 +1624,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
* to its bottom. Verify again the same set of directories
* with check_only set.
*/
- return read_directory_recursive(dir, path->buf, path->len,
+ return read_directory_recursive(dir, istate, path->buf, path->len,
cdir->ucd, 1, pathspec);
/*
* We get path_recurse in the first run when
@@ -1612,6 +1638,7 @@ static enum path_treatment treat_path_fast(struct dir_struct *dir,
static enum path_treatment treat_path(struct dir_struct *dir,
struct untracked_cache_dir *untracked,
struct cached_dir *cdir,
+ struct index_state *istate,
struct strbuf *path,
int baselen,
const struct pathspec *pathspec)
@@ -1620,7 +1647,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
struct dirent *de = cdir->de;
if (!de)
- return treat_path_fast(dir, untracked, cdir, path,
+ return treat_path_fast(dir, untracked, cdir, istate, path,
baselen, pathspec);
if (is_dot_or_dotdot(de->d_name) || !strcmp(de->d_name, ".git"))
return path_none;
@@ -1630,7 +1657,7 @@ static enum path_treatment treat_path(struct dir_struct *dir,
return path_none;
dtype = DTYPE(de);
- return treat_one_path(dir, untracked, path, baselen, pathspec, dtype, de);
+ return treat_one_path(dir, untracked, istate, path, baselen, pathspec, dtype, de);
}
static void add_untracked(struct untracked_cache_dir *dir, const char *name)
@@ -1644,6 +1671,7 @@ static void add_untracked(struct untracked_cache_dir *dir, const char *name)
static int valid_cached_dir(struct dir_struct *dir,
struct untracked_cache_dir *untracked,
+ struct index_state *istate,
struct strbuf *path,
int check_only)
{
@@ -1658,7 +1686,7 @@ static int valid_cached_dir(struct dir_struct *dir,
return 0;
}
if (!untracked->valid ||
- match_stat_data_racy(&the_index, &untracked->stat_data, &st)) {
+ match_stat_data_racy(istate, &untracked->stat_data, &st)) {
if (untracked->valid)
invalidate_directory(dir->untracked, untracked);
fill_stat_data(&untracked->stat_data, &st);
@@ -1679,10 +1707,10 @@ static int valid_cached_dir(struct dir_struct *dir,
*/
if (path->len && path->buf[path->len - 1] != '/') {
strbuf_addch(path, '/');
- prep_exclude(dir, path->buf, path->len);
+ prep_exclude(dir, istate, path->buf, path->len);
strbuf_setlen(path, path->len - 1);
} else
- prep_exclude(dir, path->buf, path->len);
+ prep_exclude(dir, istate, path->buf, path->len);
/* hopefully prep_exclude() haven't invalidated this entry... */
return untracked->valid;
@@ -1691,12 +1719,13 @@ static int valid_cached_dir(struct dir_struct *dir,
static int open_cached_dir(struct cached_dir *cdir,
struct dir_struct *dir,
struct untracked_cache_dir *untracked,
+ struct index_state *istate,
struct strbuf *path,
int check_only)
{
memset(cdir, 0, sizeof(*cdir));
cdir->untracked = untracked;
- if (valid_cached_dir(dir, untracked, path, check_only))
+ if (valid_cached_dir(dir, untracked, istate, path, check_only))
return 0;
cdir->fdir = opendir(path->len ? path->buf : ".");
if (dir->untracked)
@@ -1759,9 +1788,9 @@ static void close_cached_dir(struct cached_dir *cdir)
* Returns the most significant path_treatment value encountered in the scan.
*/
static enum path_treatment read_directory_recursive(struct dir_struct *dir,
- const char *base, int baselen,
- struct untracked_cache_dir *untracked, int check_only,
- const struct pathspec *pathspec)
+ struct index_state *istate, const char *base, int baselen,
+ struct untracked_cache_dir *untracked, int check_only,
+ const struct pathspec *pathspec)
{
struct cached_dir cdir;
enum path_treatment state, subdir_state, dir_state = path_none;
@@ -1769,7 +1798,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
strbuf_add(&path, base, baselen);
- if (open_cached_dir(&cdir, dir, untracked, &path, check_only))
+ if (open_cached_dir(&cdir, dir, untracked, istate, &path, check_only))
goto out;
if (untracked)
@@ -1777,20 +1806,23 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
while (!read_cached_dir(&cdir)) {
/* check how the file or directory should be treated */
- state = treat_path(dir, untracked, &cdir, &path,
+ state = treat_path(dir, untracked, &cdir, istate, &path,
baselen, pathspec);
if (state > dir_state)
dir_state = state;
/* recurse into subdir if instructed by treat_path */
- if (state == path_recurse) {
+ if ((state == path_recurse) ||
+ ((state == path_untracked) &&
+ (dir->flags & DIR_SHOW_IGNORED_TOO) &&
+ (get_dtype(cdir.de, istate, path.buf, path.len) == DT_DIR))) {
struct untracked_cache_dir *ud;
ud = lookup_untracked(dir->untracked, untracked,
path.buf + baselen,
path.len - baselen);
subdir_state =
- read_directory_recursive(dir, path.buf,
+ read_directory_recursive(dir, istate, path.buf,
path.len, ud,
check_only, pathspec);
if (subdir_state > dir_state)
@@ -1812,18 +1844,18 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
switch (state) {
case path_excluded:
if (dir->flags & DIR_SHOW_IGNORED)
- dir_add_name(dir, path.buf, path.len);
+ dir_add_name(dir, istate, path.buf, path.len);
else if ((dir->flags & DIR_SHOW_IGNORED_TOO) ||
((dir->flags & DIR_COLLECT_IGNORED) &&
exclude_matches_pathspec(path.buf, path.len,
pathspec)))
- dir_add_ignored(dir, path.buf, path.len);
+ dir_add_ignored(dir, istate, path.buf, path.len);
break;
case path_untracked:
if (dir->flags & DIR_SHOW_IGNORED)
break;
- dir_add_name(dir, path.buf, path.len);
+ dir_add_name(dir, istate, path.buf, path.len);
if (cdir.fdir)
add_untracked(untracked, path.buf + baselen);
break;
@@ -1839,7 +1871,7 @@ static enum path_treatment read_directory_recursive(struct dir_struct *dir,
return dir_state;
}
-static int cmp_name(const void *p1, const void *p2)
+int cmp_dir_entry(const void *p1, const void *p2)
{
const struct dir_entry *e1 = *(const struct dir_entry **)p1;
const struct dir_entry *e2 = *(const struct dir_entry **)p2;
@@ -1847,7 +1879,16 @@ static int cmp_name(const void *p1, const void *p2)
return name_compare(e1->name, e1->len, e2->name, e2->len);
}
+/* check if *out lexically strictly contains *in */
+int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in)
+{
+ return (out->len < in->len) &&
+ (out->name[out->len - 1] == '/') &&
+ !memcmp(out->name, in->name, out->len);
+}
+
static int treat_leading_path(struct dir_struct *dir,
+ struct index_state *istate,
const char *path, int len,
const struct pathspec *pathspec)
{
@@ -1875,7 +1916,7 @@ static int treat_leading_path(struct dir_struct *dir,
break;
if (simplify_away(sb.buf, sb.len, pathspec))
break;
- if (treat_one_path(dir, NULL, &sb, baselen, pathspec,
+ if (treat_one_path(dir, NULL, istate, &sb, baselen, pathspec,
DT_DIR, NULL) == path_none)
break; /* do not recurse into it */
if (len <= baselen) {
@@ -2043,8 +2084,8 @@ static struct untracked_cache_dir *validate_untracked_cache(struct dir_struct *d
return root;
}
-int read_directory(struct dir_struct *dir, const char *path,
- int len, const struct pathspec *pathspec)
+int read_directory(struct dir_struct *dir, struct index_state *istate,
+ const char *path, int len, const struct pathspec *pathspec)
{
struct untracked_cache_dir *untracked;
@@ -2058,10 +2099,34 @@ int read_directory(struct dir_struct *dir, const char *path,
* e.g. prep_exclude()
*/
dir->untracked = NULL;
- if (!len || treat_leading_path(dir, path, len, pathspec))
- read_directory_recursive(dir, path, len, untracked, 0, pathspec);
- QSORT(dir->entries, dir->nr, cmp_name);
- QSORT(dir->ignored, dir->ignored_nr, cmp_name);
+ if (!len || treat_leading_path(dir, istate, path, len, pathspec))
+ read_directory_recursive(dir, istate, path, len, untracked, 0, pathspec);
+ QSORT(dir->entries, dir->nr, cmp_dir_entry);
+ QSORT(dir->ignored, dir->ignored_nr, cmp_dir_entry);
+
+ /*
+ * If DIR_SHOW_IGNORED_TOO is set, read_directory_recursive() will
+ * also pick up untracked contents of untracked dirs; by default
+ * we discard these, but given DIR_KEEP_UNTRACKED_CONTENTS we do not.
+ */
+ if ((dir->flags & DIR_SHOW_IGNORED_TOO) &&
+ !(dir->flags & DIR_KEEP_UNTRACKED_CONTENTS)) {
+ int i, j;
+
+ /* remove from dir->entries untracked contents of untracked dirs */
+ for (i = j = 0; j < dir->nr; j++) {
+ if (i &&
+ check_dir_entry_contains(dir->entries[i - 1], dir->entries[j])) {
+ free(dir->entries[j]);
+ dir->entries[j] = NULL;
+ } else {
+ dir->entries[i++] = dir->entries[j];
+ }
+ }
+
+ dir->nr = i;
+ }
+
if (dir->untracked) {
static struct trace_key trace_untracked_stats = TRACE_KEY_INIT(UNTRACKED_STATS);
trace_printf_key(&trace_untracked_stats,
@@ -2073,12 +2138,12 @@ int read_directory(struct dir_struct *dir, const char *path,
dir->untracked->gitignore_invalidated,
dir->untracked->dir_invalidated,
dir->untracked->dir_opened);
- if (dir->untracked == the_index.untracked &&
+ if (dir->untracked == istate->untracked &&
(dir->untracked->dir_opened ||
dir->untracked->gitignore_invalidated ||
dir->untracked->dir_invalidated))
- the_index.cache_changed |= UNTRACKED_CHANGED;
- if (dir->untracked != the_index.untracked) {
+ istate->cache_changed |= UNTRACKED_CHANGED;
+ if (dir->untracked != istate->untracked) {
free(dir->untracked);
dir->untracked = NULL;
}
diff --git a/dir.h b/dir.h
index bf23a470af..a89c13e27a 100644
--- a/dir.h
+++ b/dir.h
@@ -151,7 +151,8 @@ struct dir_struct {
DIR_NO_GITLINKS = 1<<3,
DIR_COLLECT_IGNORED = 1<<4,
DIR_SHOW_IGNORED_TOO = 1<<5,
- DIR_COLLECT_KILLED_ONLY = 1<<6
+ DIR_COLLECT_KILLED_ONLY = 1<<6,
+ DIR_KEEP_UNTRACKED_CONTENTS = 1<<7
} flags;
struct dir_entry **entries;
struct dir_entry **ignored;
@@ -214,12 +215,20 @@ extern int match_pathspec(const struct pathspec *pathspec,
extern int report_path_error(const char *ps_matched, const struct pathspec *pathspec, const char *prefix);
extern int within_depth(const char *name, int namelen, int depth, int max_depth);
-extern int fill_directory(struct dir_struct *dir, const struct pathspec *pathspec);
-extern int read_directory(struct dir_struct *, const char *path, int len, const struct pathspec *pathspec);
-
-extern int is_excluded_from_list(const char *pathname, int pathlen, const char *basename,
- int *dtype, struct exclude_list *el);
-struct dir_entry *dir_add_ignored(struct dir_struct *dir, const char *pathname, int len);
+extern int fill_directory(struct dir_struct *dir,
+ struct index_state *istate,
+ const struct pathspec *pathspec);
+extern int read_directory(struct dir_struct *, struct index_state *istate,
+ const char *path, int len,
+ const struct pathspec *pathspec);
+
+extern int is_excluded_from_list(const char *pathname, int pathlen,
+ const char *basename, int *dtype,
+ struct exclude_list *el,
+ struct index_state *istate);
+struct dir_entry *dir_add_ignored(struct dir_struct *dir,
+ struct index_state *istate,
+ const char *pathname, int len);
/*
* these implement the matching logic for dir.c:excluded_from_list and
@@ -232,14 +241,17 @@ extern int match_pathname(const char *, int,
const char *, int, int, unsigned);
extern struct exclude *last_exclude_matching(struct dir_struct *dir,
+ struct index_state *istate,
const char *name, int *dtype);
-extern int is_excluded(struct dir_struct *dir, const char *name, int *dtype);
+extern int is_excluded(struct dir_struct *dir,
+ struct index_state *istate,
+ const char *name, int *dtype);
extern struct exclude_list *add_exclude_list(struct dir_struct *dir,
int group_type, const char *src);
extern int add_excludes_from_file_to_list(const char *fname, const char *base, int baselen,
- struct exclude_list *el, int check_index);
+ struct exclude_list *el, struct index_state *istate);
extern void add_excludes_from_file(struct dir_struct *, const char *fname);
extern void parse_exclude_pattern(const char **string, int *patternlen, unsigned *flags, int *nowildcardlen);
extern void add_exclude(const char *string, const char *base,
@@ -326,6 +338,9 @@ static inline int dir_path_match(const struct dir_entry *ent,
has_trailing_dir);
}
+int cmp_dir_entry(const void *p1, const void *p2);
+int check_dir_entry_contains(const struct dir_entry *out, const struct dir_entry *in);
+
void untracked_cache_invalidate_path(struct index_state *, const char *);
void untracked_cache_remove_from_index(struct index_state *, const char *);
void untracked_cache_add_to_index(struct index_state *, const char *);
diff --git a/entry.c b/entry.c
index d2b512da90..d6b263f78e 100644
--- a/entry.c
+++ b/entry.c
@@ -208,7 +208,8 @@ static int write_entry(struct cache_entry *ce,
sub = submodule_from_ce(ce);
if (sub)
return submodule_move_head(ce->name,
- NULL, oid_to_hex(&ce->oid), SUBMODULE_MOVE_HEAD_FORCE);
+ NULL, oid_to_hex(&ce->oid),
+ state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
break;
default:
return error("unknown file mode for %s in index", path);
@@ -282,12 +283,11 @@ int checkout_entry(struct cache_entry *ce,
unlink_or_warn(ce->name);
return submodule_move_head(ce->name,
- NULL, oid_to_hex(&ce->oid),
- SUBMODULE_MOVE_HEAD_FORCE);
+ NULL, oid_to_hex(&ce->oid), 0);
} else
return submodule_move_head(ce->name,
"HEAD", oid_to_hex(&ce->oid),
- SUBMODULE_MOVE_HEAD_FORCE);
+ state->force ? SUBMODULE_MOVE_HEAD_FORCE : 0);
}
if (!changed)
diff --git a/environment.c b/environment.c
index ff6e4f06e9..aa478e71de 100644
--- a/environment.c
+++ b/environment.c
@@ -169,7 +169,7 @@ static void setup_git_env(void)
git_dir = getenv(GIT_DIR_ENVIRONMENT);
if (!git_dir) {
if (!startup_info->have_repository)
- die("BUG: setup_git_env called without repository");
+ BUG("setup_git_env called without repository");
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
}
gitfile = read_gitfile(git_dir);
@@ -217,6 +217,8 @@ const char *get_git_dir(void)
const char *get_git_common_dir(void)
{
+ if (!git_dir)
+ setup_git_env();
return git_common_dir;
}
diff --git a/fast-import.c b/fast-import.c
index cf58f875b8..e69d219682 100644
--- a/fast-import.c
+++ b/fast-import.c
@@ -226,7 +226,7 @@ struct tree_entry {
struct atom_str *name;
struct tree_entry_ms {
uint16_t mode;
- unsigned char sha1[20];
+ struct object_id oid;
} versions[2];
};
@@ -252,19 +252,19 @@ struct branch {
unsigned active : 1;
unsigned delete : 1;
unsigned pack_id : PACK_ID_BITS;
- unsigned char sha1[20];
+ struct object_id oid;
};
struct tag {
struct tag *next_tag;
const char *name;
unsigned int pack_id;
- unsigned char sha1[20];
+ struct object_id oid;
};
struct hash_list {
struct hash_list *next;
- unsigned char sha1[20];
+ struct object_id oid;
};
typedef enum {
@@ -386,13 +386,15 @@ static void write_branch_report(FILE *rpt, struct branch *b)
fputs(" active", rpt);
if (b->branch_tree.tree)
fputs(" loaded", rpt);
- if (is_null_sha1(b->branch_tree.versions[1].sha1))
+ if (is_null_oid(&b->branch_tree.versions[1].oid))
fputs(" dirty", rpt);
fputc('\n', rpt);
- fprintf(rpt, " tip commit : %s\n", sha1_to_hex(b->sha1));
- fprintf(rpt, " old tree : %s\n", sha1_to_hex(b->branch_tree.versions[0].sha1));
- fprintf(rpt, " cur tree : %s\n", sha1_to_hex(b->branch_tree.versions[1].sha1));
+ fprintf(rpt, " tip commit : %s\n", oid_to_hex(&b->oid));
+ fprintf(rpt, " old tree : %s\n",
+ oid_to_hex(&b->branch_tree.versions[0].oid));
+ fprintf(rpt, " cur tree : %s\n",
+ oid_to_hex(&b->branch_tree.versions[1].oid));
fprintf(rpt, " commit clock: %" PRIuMAX "\n", b->last_commit);
fputs(" last pack : ", rpt);
@@ -470,7 +472,7 @@ static void write_crash_report(const char *err)
fputs("Annotated Tags\n", rpt);
fputs("--------------\n", rpt);
for (tg = first_tag; tg; tg = tg->next_tag) {
- fputs(sha1_to_hex(tg->sha1), rpt);
+ fputs(oid_to_hex(&tg->oid), rpt);
fputc(' ', rpt);
fputs(tg->name, rpt);
fputc('\n', rpt);
@@ -555,7 +557,7 @@ static void alloc_objects(unsigned int cnt)
alloc_count += cnt;
}
-static struct object_entry *new_object(unsigned char *sha1)
+static struct object_entry *new_object(struct object_id *oid)
{
struct object_entry *e;
@@ -563,32 +565,32 @@ static struct object_entry *new_object(unsigned char *sha1)
alloc_objects(object_entry_alloc);
e = blocks->next_free++;
- hashcpy(e->idx.sha1, sha1);
+ oidcpy(&e->idx.oid, oid);
return e;
}
-static struct object_entry *find_object(unsigned char *sha1)
+static struct object_entry *find_object(struct object_id *oid)
{
- unsigned int h = sha1[0] << 8 | sha1[1];
+ unsigned int h = oid->hash[0] << 8 | oid->hash[1];
struct object_entry *e;
for (e = object_table[h]; e; e = e->next)
- if (!hashcmp(sha1, e->idx.sha1))
+ if (!oidcmp(oid, &e->idx.oid))
return e;
return NULL;
}
-static struct object_entry *insert_object(unsigned char *sha1)
+static struct object_entry *insert_object(struct object_id *oid)
{
- unsigned int h = sha1[0] << 8 | sha1[1];
+ unsigned int h = oid->hash[0] << 8 | oid->hash[1];
struct object_entry *e = object_table[h];
while (e) {
- if (!hashcmp(sha1, e->idx.sha1))
+ if (!oidcmp(oid, &e->idx.oid))
return e;
e = e->next;
}
- e = new_object(sha1);
+ e = new_object(oid);
e->next = object_table[h];
e->idx.offset = 0;
object_table[h] = e;
@@ -876,7 +878,7 @@ static struct tree_content *dup_tree_content(struct tree_content *s)
a = s->entries[i];
b = new_tree_entry();
memcpy(b, a, sizeof(*a));
- if (a->tree && is_null_sha1(b->versions[1].sha1))
+ if (a->tree && is_null_oid(&b->versions[1].oid))
b->tree = dup_tree_content(a->tree);
else
b->tree = NULL;
@@ -1005,17 +1007,17 @@ static void end_packfile(void)
clear_delta_base_cache();
if (object_count) {
struct packed_git *new_p;
- unsigned char cur_pack_sha1[20];
+ struct object_id cur_pack_oid;
char *idx_name;
int i;
struct branch *b;
struct tag *t;
close_pack_windows(pack_data);
- sha1close(pack_file, cur_pack_sha1, 0);
+ sha1close(pack_file, cur_pack_oid.hash, 0);
fixup_pack_header_footer(pack_data->pack_fd, pack_data->sha1,
pack_data->pack_name, object_count,
- cur_pack_sha1, pack_size);
+ cur_pack_oid.hash, pack_size);
if (object_count <= unpack_limit) {
if (!loosen_small_pack(pack_data)) {
@@ -1041,12 +1043,14 @@ static void end_packfile(void)
for (i = 0; i < branch_table_sz; i++) {
for (b = branch_table[i]; b; b = b->table_next_branch) {
if (b->pack_id == pack_id)
- fprintf(pack_edges, " %s", sha1_to_hex(b->sha1));
+ fprintf(pack_edges, " %s",
+ oid_to_hex(&b->oid));
}
}
for (t = first_tag; t; t = t->next_tag) {
if (t->pack_id == pack_id)
- fprintf(pack_edges, " %s", sha1_to_hex(t->sha1));
+ fprintf(pack_edges, " %s",
+ oid_to_hex(&t->oid));
}
fputc('\n', pack_edges);
fflush(pack_edges);
@@ -1079,13 +1083,13 @@ static int store_object(
enum object_type type,
struct strbuf *dat,
struct last_object *last,
- unsigned char *sha1out,
+ struct object_id *oidout,
uintmax_t mark)
{
void *out, *delta;
struct object_entry *e;
unsigned char hdr[96];
- unsigned char sha1[20];
+ struct object_id oid;
unsigned long hdrlen, deltalen;
git_SHA_CTX c;
git_zstream s;
@@ -1095,17 +1099,17 @@ static int store_object(
git_SHA1_Init(&c);
git_SHA1_Update(&c, hdr, hdrlen);
git_SHA1_Update(&c, dat->buf, dat->len);
- git_SHA1_Final(sha1, &c);
- if (sha1out)
- hashcpy(sha1out, sha1);
+ git_SHA1_Final(oid.hash, &c);
+ if (oidout)
+ oidcpy(oidout, &oid);
- e = insert_object(sha1);
+ e = insert_object(&oid);
if (mark)
insert_mark(mark, e);
if (e->idx.offset) {
duplicate_count_by_type[type]++;
return 1;
- } else if (find_sha1_pack(sha1, packed_git)) {
+ } else if (find_sha1_pack(oid.hash, packed_git)) {
e->type = type;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
@@ -1218,13 +1222,13 @@ static void truncate_pack(struct sha1file_checkpoint *checkpoint)
pack_size = checkpoint->offset;
}
-static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
+static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)
{
size_t in_sz = 64 * 1024, out_sz = 64 * 1024;
unsigned char *in_buf = xmalloc(in_sz);
unsigned char *out_buf = xmalloc(out_sz);
struct object_entry *e;
- unsigned char sha1[20];
+ struct object_id oid;
unsigned long hdrlen;
off_t offset;
git_SHA_CTX c;
@@ -1287,12 +1291,12 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
}
}
git_deflate_end(&s);
- git_SHA1_Final(sha1, &c);
+ git_SHA1_Final(oid.hash, &c);
- if (sha1out)
- hashcpy(sha1out, sha1);
+ if (oidout)
+ oidcpy(oidout, &oid);
- e = insert_object(sha1);
+ e = insert_object(&oid);
if (mark)
insert_mark(mark, e);
@@ -1301,7 +1305,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
duplicate_count_by_type[OBJ_BLOB]++;
truncate_pack(&checkpoint);
- } else if (find_sha1_pack(sha1, packed_git)) {
+ } else if (find_sha1_pack(oid.hash, packed_git)) {
e->type = OBJ_BLOB;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
@@ -1385,7 +1389,7 @@ static const char *get_mode(const char *str, uint16_t *modep)
static void load_tree(struct tree_entry *root)
{
- unsigned char *sha1 = root->versions[1].sha1;
+ struct object_id *oid = &root->versions[1].oid;
struct object_entry *myoe;
struct tree_content *t;
unsigned long size;
@@ -1393,22 +1397,22 @@ static void load_tree(struct tree_entry *root)
const char *c;
root->tree = t = new_tree_content(8);
- if (is_null_sha1(sha1))
+ if (is_null_oid(oid))
return;
- myoe = find_object(sha1);
+ myoe = find_object(oid);
if (myoe && myoe->pack_id != MAX_PACK_ID) {
if (myoe->type != OBJ_TREE)
- die("Not a tree: %s", sha1_to_hex(sha1));
+ die("Not a tree: %s", oid_to_hex(oid));
t->delta_depth = myoe->depth;
buf = gfi_unpack_entry(myoe, &size);
if (!buf)
- die("Can't load tree %s", sha1_to_hex(sha1));
+ die("Can't load tree %s", oid_to_hex(oid));
} else {
enum object_type type;
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
if (!buf || type != OBJ_TREE)
- die("Can't load tree %s", sha1_to_hex(sha1));
+ die("Can't load tree %s", oid_to_hex(oid));
}
c = buf;
@@ -1422,13 +1426,13 @@ static void load_tree(struct tree_entry *root)
e->tree = NULL;
c = get_mode(c, &e->versions[1].mode);
if (!c)
- die("Corrupt mode in %s", sha1_to_hex(sha1));
+ die("Corrupt mode in %s", oid_to_hex(oid));
e->versions[0].mode = e->versions[1].mode;
e->name = to_atom(c, strlen(c));
c += e->name->str_len + 1;
- hashcpy(e->versions[0].sha1, (unsigned char *)c);
- hashcpy(e->versions[1].sha1, (unsigned char *)c);
- c += 20;
+ hashcpy(e->versions[0].oid.hash, (unsigned char *)c);
+ hashcpy(e->versions[1].oid.hash, (unsigned char *)c);
+ c += GIT_SHA1_RAWSZ;
}
free(buf);
}
@@ -1475,7 +1479,7 @@ static void mktree(struct tree_content *t, int v, struct strbuf *b)
strbuf_addf(b, "%o %s%c",
(unsigned int)(e->versions[v].mode & ~NO_DELTA),
e->name->str_dat, '\0');
- strbuf_add(b, e->versions[v].sha1, 20);
+ strbuf_add(b, e->versions[v].oid.hash, GIT_SHA1_RAWSZ);
}
}
@@ -1486,7 +1490,7 @@ static void store_tree(struct tree_entry *root)
struct last_object lo = { STRBUF_INIT, 0, 0, /* no_swap */ 1 };
struct object_entry *le = NULL;
- if (!is_null_sha1(root->versions[1].sha1))
+ if (!is_null_oid(&root->versions[1].oid))
return;
if (!root->tree)
@@ -1499,7 +1503,7 @@ static void store_tree(struct tree_entry *root)
}
if (!(root->versions[0].mode & NO_DELTA))
- le = find_object(root->versions[0].sha1);
+ le = find_object(&root->versions[0].oid);
if (S_ISDIR(root->versions[0].mode) && le && le->pack_id == pack_id) {
mktree(t, 0, &old_tree);
lo.data = old_tree;
@@ -1508,14 +1512,14 @@ static void store_tree(struct tree_entry *root)
}
mktree(t, 1, &new_tree);
- store_object(OBJ_TREE, &new_tree, &lo, root->versions[1].sha1, 0);
+ store_object(OBJ_TREE, &new_tree, &lo, &root->versions[1].oid, 0);
t->delta_depth = lo.depth;
for (i = 0, j = 0, del = 0; i < t->entry_count; i++) {
struct tree_entry *e = t->entries[i];
if (e->versions[1].mode) {
e->versions[0].mode = e->versions[1].mode;
- hashcpy(e->versions[0].sha1, e->versions[1].sha1);
+ oidcpy(&e->versions[0].oid, &e->versions[1].oid);
t->entries[j++] = e;
} else {
release_tree_entry(e);
@@ -1527,14 +1531,14 @@ static void store_tree(struct tree_entry *root)
static void tree_content_replace(
struct tree_entry *root,
- const unsigned char *sha1,
+ const struct object_id *oid,
const uint16_t mode,
struct tree_content *newtree)
{
if (!S_ISDIR(mode))
die("Root cannot be a non-directory");
- hashclr(root->versions[0].sha1);
- hashcpy(root->versions[1].sha1, sha1);
+ oidclr(&root->versions[0].oid);
+ oidcpy(&root->versions[1].oid, oid);
if (root->tree)
release_tree_content_recursive(root->tree);
root->tree = newtree;
@@ -1543,7 +1547,7 @@ static void tree_content_replace(
static int tree_content_set(
struct tree_entry *root,
const char *p,
- const unsigned char *sha1,
+ const struct object_id *oid,
const uint16_t mode,
struct tree_content *subtree)
{
@@ -1568,10 +1572,10 @@ static int tree_content_set(
if (!*slash1) {
if (!S_ISDIR(mode)
&& e->versions[1].mode == mode
- && !hashcmp(e->versions[1].sha1, sha1))
+ && !oidcmp(&e->versions[1].oid, oid))
return 0;
e->versions[1].mode = mode;
- hashcpy(e->versions[1].sha1, sha1);
+ oidcpy(&e->versions[1].oid, oid);
if (e->tree)
release_tree_content_recursive(e->tree);
e->tree = subtree;
@@ -1592,7 +1596,7 @@ static int tree_content_set(
if (S_ISDIR(e->versions[0].mode))
e->versions[0].mode |= NO_DELTA;
- hashclr(root->versions[1].sha1);
+ oidclr(&root->versions[1].oid);
return 1;
}
if (!S_ISDIR(e->versions[1].mode)) {
@@ -1601,8 +1605,8 @@ static int tree_content_set(
}
if (!e->tree)
load_tree(e);
- if (tree_content_set(e, slash1 + 1, sha1, mode, subtree)) {
- hashclr(root->versions[1].sha1);
+ if (tree_content_set(e, slash1 + 1, oid, mode, subtree)) {
+ oidclr(&root->versions[1].oid);
return 1;
}
return 0;
@@ -1614,18 +1618,18 @@ static int tree_content_set(
e = new_tree_entry();
e->name = to_atom(p, n);
e->versions[0].mode = 0;
- hashclr(e->versions[0].sha1);
+ oidclr(&e->versions[0].oid);
t->entries[t->entry_count++] = e;
if (*slash1) {
e->tree = new_tree_content(8);
e->versions[1].mode = S_IFDIR;
- tree_content_set(e, slash1 + 1, sha1, mode, subtree);
+ tree_content_set(e, slash1 + 1, oid, mode, subtree);
} else {
e->tree = subtree;
e->versions[1].mode = mode;
- hashcpy(e->versions[1].sha1, sha1);
+ oidcpy(&e->versions[1].oid, oid);
}
- hashclr(root->versions[1].sha1);
+ oidclr(&root->versions[1].oid);
return 1;
}
@@ -1670,7 +1674,7 @@ static int tree_content_remove(
if (tree_content_remove(e, slash1 + 1, backup_leaf, 0)) {
for (n = 0; n < e->tree->entry_count; n++) {
if (e->tree->entries[n]->versions[1].mode) {
- hashclr(root->versions[1].sha1);
+ oidclr(&root->versions[1].oid);
return 1;
}
}
@@ -1689,8 +1693,8 @@ del_entry:
release_tree_content_recursive(e->tree);
e->tree = NULL;
e->versions[1].mode = 0;
- hashclr(e->versions[1].sha1);
- hashclr(root->versions[1].sha1);
+ oidclr(&e->versions[1].oid);
+ oidclr(&root->versions[1].oid);
return 1;
}
@@ -1735,7 +1739,7 @@ static int tree_content_get(
found_entry:
memcpy(leaf, e, sizeof(*leaf));
- if (e->tree && is_null_sha1(e->versions[1].sha1))
+ if (e->tree && is_null_oid(&e->versions[1].oid))
leaf->tree = dup_tree_content(e->tree);
else
leaf->tree = NULL;
@@ -1746,34 +1750,35 @@ static int update_branch(struct branch *b)
{
static const char *msg = "fast-import";
struct ref_transaction *transaction;
- unsigned char old_sha1[20];
+ struct object_id old_oid;
struct strbuf err = STRBUF_INIT;
- if (is_null_sha1(b->sha1)) {
+ if (is_null_oid(&b->oid)) {
if (b->delete)
delete_ref(NULL, b->name, NULL, 0);
return 0;
}
- if (read_ref(b->name, old_sha1))
- hashclr(old_sha1);
- if (!force_update && !is_null_sha1(old_sha1)) {
+ if (read_ref(b->name, old_oid.hash))
+ oidclr(&old_oid);
+ if (!force_update && !is_null_oid(&old_oid)) {
struct commit *old_cmit, *new_cmit;
- old_cmit = lookup_commit_reference_gently(old_sha1, 0);
- new_cmit = lookup_commit_reference_gently(b->sha1, 0);
+ old_cmit = lookup_commit_reference_gently(&old_oid, 0);
+ new_cmit = lookup_commit_reference_gently(&b->oid, 0);
if (!old_cmit || !new_cmit)
return error("Branch %s is missing commits.", b->name);
if (!in_merge_bases(old_cmit, new_cmit)) {
warning("Not updating %s"
" (new tip %s does not contain %s)",
- b->name, sha1_to_hex(b->sha1), sha1_to_hex(old_sha1));
+ b->name, oid_to_hex(&b->oid),
+ oid_to_hex(&old_oid));
return -1;
}
}
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_update(transaction, b->name, b->sha1, old_sha1,
+ ref_transaction_update(transaction, b->name, b->oid.hash, old_oid.hash,
0, msg, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
@@ -1815,7 +1820,7 @@ static void dump_tags(void)
strbuf_addf(&ref_name, "refs/tags/%s", t->name);
if (ref_transaction_update(transaction, ref_name.buf,
- t->sha1, NULL, 0, msg, &err)) {
+ t->oid.hash, NULL, 0, msg, &err)) {
failure |= error("%s", err.buf);
goto cleanup;
}
@@ -1844,7 +1849,7 @@ static void dump_marks_helper(FILE *f,
for (k = 0; k < 1024; k++) {
if (m->data.marked[k])
fprintf(f, ":%" PRIuMAX " %s\n", base + k,
- sha1_to_hex(m->data.marked[k]->idx.sha1));
+ oid_to_hex(&m->data.marked[k]->idx.oid));
}
}
}
@@ -1893,7 +1898,7 @@ static void read_marks(void)
while (fgets(line, sizeof(line), f)) {
uintmax_t mark;
char *end;
- unsigned char sha1[20];
+ struct object_id oid;
struct object_entry *e;
end = strchr(line, '\n');
@@ -1902,14 +1907,14 @@ static void read_marks(void)
*end = 0;
mark = strtoumax(line + 1, &end, 10);
if (!mark || end == line + 1
- || *end != ' ' || get_sha1_hex(end + 1, sha1))
+ || *end != ' ' || get_oid_hex(end + 1, &oid))
die("corrupt mark line: %s", line);
- e = find_object(sha1);
+ e = find_object(&oid);
if (!e) {
- enum object_type type = sha1_object_info(sha1, NULL);
+ enum object_type type = sha1_object_info(oid.hash, NULL);
if (type < 0)
- die("object not found: %s", sha1_to_hex(sha1));
- e = insert_object(sha1);
+ die("object not found: %s", oid_to_hex(&oid));
+ e = insert_object(&oid);
e->type = type;
e->pack_id = MAX_PACK_ID;
e->idx.offset = 1; /* just not zero! */
@@ -2117,21 +2122,21 @@ static char *parse_ident(const char *buf)
static void parse_and_store_blob(
struct last_object *last,
- unsigned char *sha1out,
+ struct object_id *oidout,
uintmax_t mark)
{
static struct strbuf buf = STRBUF_INIT;
uintmax_t len;
if (parse_data(&buf, big_file_threshold, &len))
- store_object(OBJ_BLOB, &buf, last, sha1out, mark);
+ store_object(OBJ_BLOB, &buf, last, oidout, mark);
else {
if (last) {
strbuf_release(&last->data);
last->offset = 0;
last->depth = 0;
}
- stream_blob(len, sha1out, mark);
+ stream_blob(len, oidout, mark);
skip_optional_lf();
}
}
@@ -2207,21 +2212,21 @@ static void construct_path_with_fanout(const char *hex_sha1,
path[i++] = '/';
fanout--;
}
- memcpy(path + i, hex_sha1 + j, 40 - j);
- path[i + 40 - j] = '\0';
+ memcpy(path + i, hex_sha1 + j, GIT_SHA1_HEXSZ - j);
+ path[i + GIT_SHA1_HEXSZ - j] = '\0';
}
static uintmax_t do_change_note_fanout(
struct tree_entry *orig_root, struct tree_entry *root,
- char *hex_sha1, unsigned int hex_sha1_len,
+ char *hex_oid, unsigned int hex_oid_len,
char *fullpath, unsigned int fullpath_len,
unsigned char fanout)
{
struct tree_content *t;
struct tree_entry *e, leaf;
- unsigned int i, tmp_hex_sha1_len, tmp_fullpath_len;
+ unsigned int i, tmp_hex_oid_len, tmp_fullpath_len;
uintmax_t num_notes = 0;
- unsigned char sha1[20];
+ struct object_id oid;
char realpath[60];
if (!root->tree)
@@ -2230,7 +2235,7 @@ static uintmax_t do_change_note_fanout(
for (i = 0; t && i < t->entry_count; i++) {
e = t->entries[i];
- tmp_hex_sha1_len = hex_sha1_len + e->name->str_len;
+ tmp_hex_oid_len = hex_oid_len + e->name->str_len;
tmp_fullpath_len = fullpath_len;
/*
@@ -2242,12 +2247,12 @@ static uintmax_t do_change_note_fanout(
* of 2 chars.
*/
if (!e->versions[1].mode ||
- tmp_hex_sha1_len > 40 ||
+ tmp_hex_oid_len > GIT_SHA1_HEXSZ ||
e->name->str_len % 2)
continue;
/* This _may_ be a note entry, or a subdir containing notes */
- memcpy(hex_sha1 + hex_sha1_len, e->name->str_dat,
+ memcpy(hex_oid + hex_oid_len, e->name->str_dat,
e->name->str_len);
if (tmp_fullpath_len)
fullpath[tmp_fullpath_len++] = '/';
@@ -2256,14 +2261,14 @@ static uintmax_t do_change_note_fanout(
tmp_fullpath_len += e->name->str_len;
fullpath[tmp_fullpath_len] = '\0';
- if (tmp_hex_sha1_len == 40 && !get_sha1_hex(hex_sha1, sha1)) {
+ if (tmp_hex_oid_len == GIT_SHA1_HEXSZ && !get_oid_hex(hex_oid, &oid)) {
/* This is a note entry */
if (fanout == 0xff) {
/* Counting mode, no rename */
num_notes++;
continue;
}
- construct_path_with_fanout(hex_sha1, fanout, realpath);
+ construct_path_with_fanout(hex_oid, fanout, realpath);
if (!strcmp(fullpath, realpath)) {
/* Note entry is in correct location */
num_notes++;
@@ -2274,13 +2279,13 @@ static uintmax_t do_change_note_fanout(
if (!tree_content_remove(orig_root, fullpath, &leaf, 0))
die("Failed to remove path %s", fullpath);
tree_content_set(orig_root, realpath,
- leaf.versions[1].sha1,
+ &leaf.versions[1].oid,
leaf.versions[1].mode,
leaf.tree);
} else if (S_ISDIR(e->versions[1].mode)) {
/* This is a subdir that may contain note entries */
num_notes += do_change_note_fanout(orig_root, e,
- hex_sha1, tmp_hex_sha1_len,
+ hex_oid, tmp_hex_oid_len,
fullpath, tmp_fullpath_len, fanout);
}
@@ -2293,8 +2298,14 @@ static uintmax_t do_change_note_fanout(
static uintmax_t change_note_fanout(struct tree_entry *root,
unsigned char fanout)
{
- char hex_sha1[40], path[60];
- return do_change_note_fanout(root, root, hex_sha1, 0, path, 0, fanout);
+ /*
+ * The size of path is due to one slash between every two hex digits,
+ * plus the terminating NUL. Note that there is no slash at the end, so
+ * the number of slashes is one less than half the number of hex
+ * characters.
+ */
+ char hex_oid[GIT_MAX_HEXSZ], path[GIT_MAX_HEXSZ + (GIT_MAX_HEXSZ / 2) - 1 + 1];
+ return do_change_note_fanout(root, root, hex_oid, 0, path, 0, fanout);
}
/*
@@ -2355,7 +2366,7 @@ static void file_change_m(const char *p, struct branch *b)
static struct strbuf uq = STRBUF_INIT;
const char *endp;
struct object_entry *oe;
- unsigned char sha1[20];
+ struct object_id oid;
uint16_t mode, inline_data = 0;
p = get_mode(p, &mode);
@@ -2378,15 +2389,14 @@ static void file_change_m(const char *p, struct branch *b)
if (*p == ':') {
oe = find_mark(parse_mark_ref_space(&p));
- hashcpy(sha1, oe->idx.sha1);
+ oidcpy(&oid, &oe->idx.oid);
} else if (skip_prefix(p, "inline ", &p)) {
inline_data = 1;
oe = NULL; /* not used with inline_data, but makes gcc happy */
} else {
- if (get_sha1_hex(p, sha1))
+ if (parse_oid_hex(p, &oid, &p))
die("Invalid dataref: %s", command_buf.buf);
- oe = find_object(sha1);
- p += 40;
+ oe = find_object(&oid);
if (*p++ != ' ')
die("Missing space after SHA1: %s", command_buf.buf);
}
@@ -2399,7 +2409,7 @@ static void file_change_m(const char *p, struct branch *b)
}
/* Git does not track empty, non-toplevel directories. */
- if (S_ISDIR(mode) && !hashcmp(sha1, EMPTY_TREE_SHA1_BIN) && *p) {
+ if (S_ISDIR(mode) && is_empty_tree_oid(&oid) && *p) {
tree_content_remove(&b->branch_tree, p, NULL, 0);
return;
}
@@ -2426,12 +2436,12 @@ static void file_change_m(const char *p, struct branch *b)
p = uq.buf;
}
read_next_command();
- parse_and_store_blob(&last_blob, sha1, 0);
+ parse_and_store_blob(&last_blob, &oid, 0);
} else {
enum object_type expected = S_ISDIR(mode) ?
OBJ_TREE: OBJ_BLOB;
enum object_type type = oe ? oe->type :
- sha1_object_info(sha1, NULL);
+ sha1_object_info(oid.hash, NULL);
if (type < 0)
die("%s not found: %s",
S_ISDIR(mode) ? "Tree" : "Blob",
@@ -2443,10 +2453,10 @@ static void file_change_m(const char *p, struct branch *b)
}
if (!*p) {
- tree_content_replace(&b->branch_tree, sha1, mode, NULL);
+ tree_content_replace(&b->branch_tree, &oid, mode, NULL);
return;
}
- tree_content_set(&b->branch_tree, p, sha1, mode, NULL);
+ tree_content_set(&b->branch_tree, p, &oid, mode, NULL);
}
static void file_change_d(const char *p, struct branch *b)
@@ -2504,13 +2514,13 @@ static void file_change_cr(const char *s, struct branch *b, int rename)
die("Path %s not in branch", s);
if (!*d) { /* C "path/to/subdir" "" */
tree_content_replace(&b->branch_tree,
- leaf.versions[1].sha1,
+ &leaf.versions[1].oid,
leaf.versions[1].mode,
leaf.tree);
return;
}
tree_content_set(&b->branch_tree, d,
- leaf.versions[1].sha1,
+ &leaf.versions[1].oid,
leaf.versions[1].mode,
leaf.tree);
}
@@ -2520,7 +2530,7 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
static struct strbuf uq = STRBUF_INIT;
struct object_entry *oe;
struct branch *s;
- unsigned char sha1[20], commit_sha1[20];
+ struct object_id oid, commit_oid;
char path[60];
uint16_t inline_data = 0;
unsigned char new_fanout;
@@ -2545,15 +2555,14 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
/* <dataref> or 'inline' */
if (*p == ':') {
oe = find_mark(parse_mark_ref_space(&p));
- hashcpy(sha1, oe->idx.sha1);
+ oidcpy(&oid, &oe->idx.oid);
} else if (skip_prefix(p, "inline ", &p)) {
inline_data = 1;
oe = NULL; /* not used with inline_data, but makes gcc happy */
} else {
- if (get_sha1_hex(p, sha1))
+ if (parse_oid_hex(p, &oid, &p))
die("Invalid dataref: %s", command_buf.buf);
- oe = find_object(sha1);
- p += 40;
+ oe = find_object(&oid);
if (*p++ != ' ')
die("Missing space after SHA1: %s", command_buf.buf);
}
@@ -2561,19 +2570,19 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
/* <commit-ish> */
s = lookup_branch(p);
if (s) {
- if (is_null_sha1(s->sha1))
+ if (is_null_oid(&s->oid))
die("Can't add a note on empty branch.");
- hashcpy(commit_sha1, s->sha1);
+ oidcpy(&commit_oid, &s->oid);
} else if (*p == ':') {
uintmax_t commit_mark = parse_mark_ref_eol(p);
struct object_entry *commit_oe = find_mark(commit_mark);
if (commit_oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", commit_mark);
- hashcpy(commit_sha1, commit_oe->idx.sha1);
- } else if (!get_sha1(p, commit_sha1)) {
+ oidcpy(&commit_oid, &commit_oe->idx.oid);
+ } else if (!get_oid(p, &commit_oid)) {
unsigned long size;
- char *buf = read_object_with_reference(commit_sha1,
- commit_type, &size, commit_sha1);
+ char *buf = read_object_with_reference(commit_oid.hash,
+ commit_type, &size, commit_oid.hash);
if (!buf || size < 46)
die("Not a valid commit: %s", p);
free(buf);
@@ -2586,13 +2595,13 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
p = uq.buf;
}
read_next_command();
- parse_and_store_blob(&last_blob, sha1, 0);
+ parse_and_store_blob(&last_blob, &oid, 0);
} else if (oe) {
if (oe->type != OBJ_BLOB)
die("Not a blob (actually a %s): %s",
typename(oe->type), command_buf.buf);
- } else if (!is_null_sha1(sha1)) {
- enum object_type type = sha1_object_info(sha1, NULL);
+ } else if (!is_null_oid(&oid)) {
+ enum object_type type = sha1_object_info(oid.hash, NULL);
if (type < 0)
die("Blob not found: %s", command_buf.buf);
if (type != OBJ_BLOB)
@@ -2600,50 +2609,51 @@ static void note_change_n(const char *p, struct branch *b, unsigned char *old_fa
typename(type), command_buf.buf);
}
- construct_path_with_fanout(sha1_to_hex(commit_sha1), *old_fanout, path);
+ construct_path_with_fanout(oid_to_hex(&commit_oid), *old_fanout, path);
if (tree_content_remove(&b->branch_tree, path, NULL, 0))
b->num_notes--;
- if (is_null_sha1(sha1))
+ if (is_null_oid(&oid))
return; /* nothing to insert */
b->num_notes++;
new_fanout = convert_num_notes_to_fanout(b->num_notes);
- construct_path_with_fanout(sha1_to_hex(commit_sha1), new_fanout, path);
- tree_content_set(&b->branch_tree, path, sha1, S_IFREG | 0644, NULL);
+ construct_path_with_fanout(oid_to_hex(&commit_oid), new_fanout, path);
+ tree_content_set(&b->branch_tree, path, &oid, S_IFREG | 0644, NULL);
}
static void file_change_deleteall(struct branch *b)
{
release_tree_content_recursive(b->branch_tree.tree);
- hashclr(b->branch_tree.versions[0].sha1);
- hashclr(b->branch_tree.versions[1].sha1);
+ oidclr(&b->branch_tree.versions[0].oid);
+ oidclr(&b->branch_tree.versions[1].oid);
load_tree(&b->branch_tree);
b->num_notes = 0;
}
static void parse_from_commit(struct branch *b, char *buf, unsigned long size)
{
- if (!buf || size < 46)
- die("Not a valid commit: %s", sha1_to_hex(b->sha1));
+ if (!buf || size < GIT_SHA1_HEXSZ + 6)
+ die("Not a valid commit: %s", oid_to_hex(&b->oid));
if (memcmp("tree ", buf, 5)
- || get_sha1_hex(buf + 5, b->branch_tree.versions[1].sha1))
- die("The commit %s is corrupt", sha1_to_hex(b->sha1));
- hashcpy(b->branch_tree.versions[0].sha1,
- b->branch_tree.versions[1].sha1);
+ || get_oid_hex(buf + 5, &b->branch_tree.versions[1].oid))
+ die("The commit %s is corrupt", oid_to_hex(&b->oid));
+ oidcpy(&b->branch_tree.versions[0].oid,
+ &b->branch_tree.versions[1].oid);
}
static void parse_from_existing(struct branch *b)
{
- if (is_null_sha1(b->sha1)) {
- hashclr(b->branch_tree.versions[0].sha1);
- hashclr(b->branch_tree.versions[1].sha1);
+ if (is_null_oid(&b->oid)) {
+ oidclr(&b->branch_tree.versions[0].oid);
+ oidclr(&b->branch_tree.versions[1].oid);
} else {
unsigned long size;
char *buf;
- buf = read_object_with_reference(b->sha1,
- commit_type, &size, b->sha1);
+ buf = read_object_with_reference(b->oid.hash,
+ commit_type, &size,
+ b->oid.hash);
parse_from_commit(b, buf, size);
free(buf);
}
@@ -2653,28 +2663,28 @@ static int parse_from(struct branch *b)
{
const char *from;
struct branch *s;
- unsigned char sha1[20];
+ struct object_id oid;
if (!skip_prefix(command_buf.buf, "from ", &from))
return 0;
- hashcpy(sha1, b->branch_tree.versions[1].sha1);
+ oidcpy(&oid, &b->branch_tree.versions[1].oid);
s = lookup_branch(from);
if (b == s)
die("Can't create a branch from itself: %s", b->name);
else if (s) {
- unsigned char *t = s->branch_tree.versions[1].sha1;
- hashcpy(b->sha1, s->sha1);
- hashcpy(b->branch_tree.versions[0].sha1, t);
- hashcpy(b->branch_tree.versions[1].sha1, t);
+ struct object_id *t = &s->branch_tree.versions[1].oid;
+ oidcpy(&b->oid, &s->oid);
+ oidcpy(&b->branch_tree.versions[0].oid, t);
+ oidcpy(&b->branch_tree.versions[1].oid, t);
} else if (*from == ':') {
uintmax_t idnum = parse_mark_ref_eol(from);
struct object_entry *oe = find_mark(idnum);
if (oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", idnum);
- if (hashcmp(b->sha1, oe->idx.sha1)) {
- hashcpy(b->sha1, oe->idx.sha1);
+ if (oidcmp(&b->oid, &oe->idx.oid)) {
+ oidcpy(&b->oid, &oe->idx.oid);
if (oe->pack_id != MAX_PACK_ID) {
unsigned long size;
char *buf = gfi_unpack_entry(oe, &size);
@@ -2683,15 +2693,15 @@ static int parse_from(struct branch *b)
} else
parse_from_existing(b);
}
- } else if (!get_sha1(from, b->sha1)) {
+ } else if (!get_oid(from, &b->oid)) {
parse_from_existing(b);
- if (is_null_sha1(b->sha1))
+ if (is_null_oid(&b->oid))
b->delete = 1;
}
else
die("Invalid ref name or SHA1 expression: %s", from);
- if (b->branch_tree.tree && hashcmp(sha1, b->branch_tree.versions[1].sha1)) {
+ if (b->branch_tree.tree && oidcmp(&oid, &b->branch_tree.versions[1].oid)) {
release_tree_content_recursive(b->branch_tree.tree);
b->branch_tree.tree = NULL;
}
@@ -2711,17 +2721,17 @@ static struct hash_list *parse_merge(unsigned int *count)
n = xmalloc(sizeof(*n));
s = lookup_branch(from);
if (s)
- hashcpy(n->sha1, s->sha1);
+ oidcpy(&n->oid, &s->oid);
else if (*from == ':') {
uintmax_t idnum = parse_mark_ref_eol(from);
struct object_entry *oe = find_mark(idnum);
if (oe->type != OBJ_COMMIT)
die("Mark :%" PRIuMAX " not a commit", idnum);
- hashcpy(n->sha1, oe->idx.sha1);
- } else if (!get_sha1(from, n->sha1)) {
+ oidcpy(&n->oid, &oe->idx.oid);
+ } else if (!get_oid(from, &n->oid)) {
unsigned long size;
- char *buf = read_object_with_reference(n->sha1,
- commit_type, &size, n->sha1);
+ char *buf = read_object_with_reference(n->oid.hash,
+ commit_type, &size, n->oid.hash);
if (!buf || size < 46)
die("Not a valid commit: %s", from);
free(buf);
@@ -2808,17 +2818,19 @@ static void parse_new_commit(const char *arg)
/* build the tree and the commit */
store_tree(&b->branch_tree);
- hashcpy(b->branch_tree.versions[0].sha1,
- b->branch_tree.versions[1].sha1);
+ oidcpy(&b->branch_tree.versions[0].oid,
+ &b->branch_tree.versions[1].oid);
strbuf_reset(&new_data);
strbuf_addf(&new_data, "tree %s\n",
- sha1_to_hex(b->branch_tree.versions[1].sha1));
- if (!is_null_sha1(b->sha1))
- strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(b->sha1));
+ oid_to_hex(&b->branch_tree.versions[1].oid));
+ if (!is_null_oid(&b->oid))
+ strbuf_addf(&new_data, "parent %s\n",
+ oid_to_hex(&b->oid));
while (merge_list) {
struct hash_list *next = merge_list->next;
- strbuf_addf(&new_data, "parent %s\n", sha1_to_hex(merge_list->sha1));
+ strbuf_addf(&new_data, "parent %s\n",
+ oid_to_hex(&merge_list->oid));
free(merge_list);
merge_list = next;
}
@@ -2831,7 +2843,7 @@ static void parse_new_commit(const char *arg)
free(author);
free(committer);
- if (!store_object(OBJ_COMMIT, &new_data, NULL, b->sha1, next_mark))
+ if (!store_object(OBJ_COMMIT, &new_data, NULL, &b->oid, next_mark))
b->pack_id = pack_id;
b->last_commit = object_count_by_type[OBJ_COMMIT];
}
@@ -2844,7 +2856,7 @@ static void parse_new_tag(const char *arg)
struct branch *s;
struct tag *t;
uintmax_t from_mark = 0;
- unsigned char sha1[20];
+ struct object_id oid;
enum object_type type;
const char *v;
@@ -2863,20 +2875,20 @@ static void parse_new_tag(const char *arg)
die("Expected from command, got %s", command_buf.buf);
s = lookup_branch(from);
if (s) {
- if (is_null_sha1(s->sha1))
+ if (is_null_oid(&s->oid))
die("Can't tag an empty branch.");
- hashcpy(sha1, s->sha1);
+ oidcpy(&oid, &s->oid);
type = OBJ_COMMIT;
} else if (*from == ':') {
struct object_entry *oe;
from_mark = parse_mark_ref_eol(from);
oe = find_mark(from_mark);
type = oe->type;
- hashcpy(sha1, oe->idx.sha1);
- } else if (!get_sha1(from, sha1)) {
- struct object_entry *oe = find_object(sha1);
+ oidcpy(&oid, &oe->idx.oid);
+ } else if (!get_oid(from, &oid)) {
+ struct object_entry *oe = find_object(&oid);
if (!oe) {
- type = sha1_object_info(sha1, NULL);
+ type = sha1_object_info(oid.hash, NULL);
if (type < 0)
die("Not a valid object: %s", from);
} else
@@ -2902,7 +2914,7 @@ static void parse_new_tag(const char *arg)
"object %s\n"
"type %s\n"
"tag %s\n",
- sha1_to_hex(sha1), typename(type), t->name);
+ oid_to_hex(&oid), typename(type), t->name);
if (tagger)
strbuf_addf(&new_data,
"tagger %s\n", tagger);
@@ -2910,7 +2922,7 @@ static void parse_new_tag(const char *arg)
strbuf_addbuf(&new_data, &msg);
free(tagger);
- if (store_object(OBJ_TAG, &new_data, NULL, t->sha1, 0))
+ if (store_object(OBJ_TAG, &new_data, NULL, &t->oid, 0))
t->pack_id = MAX_PACK_ID;
else
t->pack_id = pack_id;
@@ -2922,9 +2934,9 @@ static void parse_reset_branch(const char *arg)
b = lookup_branch(arg);
if (b) {
- hashclr(b->sha1);
- hashclr(b->branch_tree.versions[0].sha1);
- hashclr(b->branch_tree.versions[1].sha1);
+ oidclr(&b->oid);
+ oidclr(&b->branch_tree.versions[0].oid);
+ oidclr(&b->branch_tree.versions[1].oid);
if (b->branch_tree.tree) {
release_tree_content_recursive(b->branch_tree.tree);
b->branch_tree.tree = NULL;
@@ -2944,7 +2956,7 @@ static void cat_blob_write(const char *buf, unsigned long size)
die_errno("Write to frontend failed");
}
-static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
+static void cat_blob(struct object_entry *oe, struct object_id *oid)
{
struct strbuf line = STRBUF_INIT;
unsigned long size;
@@ -2952,7 +2964,7 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
char *buf;
if (!oe || oe->pack_id == MAX_PACK_ID) {
- buf = read_sha1_file(sha1, &type, &size);
+ buf = read_sha1_file(oid->hash, &type, &size);
} else {
type = oe->type;
buf = gfi_unpack_entry(oe, &size);
@@ -2963,19 +2975,19 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
*/
if (type <= 0) {
strbuf_reset(&line);
- strbuf_addf(&line, "%s missing\n", sha1_to_hex(sha1));
+ strbuf_addf(&line, "%s missing\n", oid_to_hex(oid));
cat_blob_write(line.buf, line.len);
strbuf_release(&line);
free(buf);
return;
}
if (!buf)
- die("Can't read object %s", sha1_to_hex(sha1));
+ die("Can't read object %s", oid_to_hex(oid));
if (type != OBJ_BLOB)
die("Object %s is a %s but a blob was expected.",
- sha1_to_hex(sha1), typename(type));
+ oid_to_hex(oid), typename(type));
strbuf_reset(&line);
- strbuf_addf(&line, "%s %s %lu\n", sha1_to_hex(sha1),
+ strbuf_addf(&line, "%s %s %lu\n", oid_to_hex(oid),
typename(type), size);
cat_blob_write(line.buf, line.len);
strbuf_release(&line);
@@ -2992,7 +3004,7 @@ static void cat_blob(struct object_entry *oe, unsigned char sha1[20])
static void parse_get_mark(const char *p)
{
struct object_entry *oe = oe;
- char output[42];
+ char output[GIT_MAX_HEXSZ + 2];
/* get-mark SP <object> LF */
if (*p != ':')
@@ -3002,43 +3014,43 @@ static void parse_get_mark(const char *p)
if (!oe)
die("Unknown mark: %s", command_buf.buf);
- xsnprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1));
- cat_blob_write(output, 41);
+ xsnprintf(output, sizeof(output), "%s\n", oid_to_hex(&oe->idx.oid));
+ cat_blob_write(output, GIT_SHA1_HEXSZ + 1);
}
static void parse_cat_blob(const char *p)
{
struct object_entry *oe = oe;
- unsigned char sha1[20];
+ struct object_id oid;
/* cat-blob SP <object> LF */
if (*p == ':') {
oe = find_mark(parse_mark_ref_eol(p));
if (!oe)
die("Unknown mark: %s", command_buf.buf);
- hashcpy(sha1, oe->idx.sha1);
+ oidcpy(&oid, &oe->idx.oid);
} else {
- if (get_sha1_hex(p, sha1))
+ if (parse_oid_hex(p, &oid, &p))
die("Invalid dataref: %s", command_buf.buf);
- if (p[40])
+ if (*p)
die("Garbage after SHA1: %s", command_buf.buf);
- oe = find_object(sha1);
+ oe = find_object(&oid);
}
- cat_blob(oe, sha1);
+ cat_blob(oe, &oid);
}
static struct object_entry *dereference(struct object_entry *oe,
- unsigned char sha1[20])
+ struct object_id *oid)
{
unsigned long size;
char *buf = NULL;
if (!oe) {
- enum object_type type = sha1_object_info(sha1, NULL);
+ enum object_type type = sha1_object_info(oid->hash, NULL);
if (type < 0)
- die("object not found: %s", sha1_to_hex(sha1));
+ die("object not found: %s", oid_to_hex(oid));
/* cache it! */
- oe = insert_object(sha1);
+ oe = insert_object(oid);
oe->type = type;
oe->pack_id = MAX_PACK_ID;
oe->idx.offset = 1;
@@ -3057,49 +3069,48 @@ static struct object_entry *dereference(struct object_entry *oe,
buf = gfi_unpack_entry(oe, &size);
} else {
enum object_type unused;
- buf = read_sha1_file(sha1, &unused, &size);
+ buf = read_sha1_file(oid->hash, &unused, &size);
}
if (!buf)
- die("Can't load object %s", sha1_to_hex(sha1));
+ die("Can't load object %s", oid_to_hex(oid));
/* Peel one layer. */
switch (oe->type) {
case OBJ_TAG:
- if (size < 40 + strlen("object ") ||
- get_sha1_hex(buf + strlen("object "), sha1))
+ if (size < GIT_SHA1_HEXSZ + strlen("object ") ||
+ get_oid_hex(buf + strlen("object "), oid))
die("Invalid SHA1 in tag: %s", command_buf.buf);
break;
case OBJ_COMMIT:
- if (size < 40 + strlen("tree ") ||
- get_sha1_hex(buf + strlen("tree "), sha1))
+ if (size < GIT_SHA1_HEXSZ + strlen("tree ") ||
+ get_oid_hex(buf + strlen("tree "), oid))
die("Invalid SHA1 in commit: %s", command_buf.buf);
}
free(buf);
- return find_object(sha1);
+ return find_object(oid);
}
static struct object_entry *parse_treeish_dataref(const char **p)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct object_entry *e;
if (**p == ':') { /* <mark> */
e = find_mark(parse_mark_ref_space(p));
if (!e)
die("Unknown mark: %s", command_buf.buf);
- hashcpy(sha1, e->idx.sha1);
+ oidcpy(&oid, &e->idx.oid);
} else { /* <sha1> */
- if (get_sha1_hex(*p, sha1))
+ if (parse_oid_hex(*p, &oid, p))
die("Invalid dataref: %s", command_buf.buf);
- e = find_object(sha1);
- *p += 40;
+ e = find_object(&oid);
if (*(*p)++ != ' ')
die("Missing space after tree-ish: %s", command_buf.buf);
}
while (!e || e->type != OBJ_TREE)
- e = dereference(e, sha1);
+ e = dereference(e, &oid);
return e;
}
@@ -3143,8 +3154,8 @@ static void parse_ls(const char *p, struct branch *b)
} else {
struct object_entry *e = parse_treeish_dataref(&p);
root = new_tree_entry();
- hashcpy(root->versions[1].sha1, e->idx.sha1);
- if (!is_null_sha1(root->versions[1].sha1))
+ oidcpy(&root->versions[1].oid, &e->idx.oid);
+ if (!is_null_oid(&root->versions[1].oid))
root->versions[1].mode = S_IFDIR;
load_tree(root);
}
@@ -3166,7 +3177,7 @@ static void parse_ls(const char *p, struct branch *b)
if (S_ISDIR(leaf.versions[1].mode))
store_tree(&leaf);
- print_ls(leaf.versions[1].mode, leaf.versions[1].sha1, p);
+ print_ls(leaf.versions[1].mode, leaf.versions[1].oid.hash, p);
if (leaf.tree)
release_tree_content_recursive(leaf.tree);
if (!b || root != &b->branch_tree)
diff --git a/fetch-pack.c b/fetch-pack.c
index afb8b05024..cd86865beb 100644
--- a/fetch-pack.c
+++ b/fetch-pack.c
@@ -15,6 +15,7 @@
#include "version.h"
#include "prio-queue.h"
#include "sha1-array.h"
+#include "oidset.h"
static int transfer_unpack_limit = -1;
static int fetch_unpack_limit = -1;
@@ -78,7 +79,7 @@ static void cache_one_alternate(const char *refname,
void *vcache)
{
struct alternate_object_cache *cache = vcache;
- struct object *obj = parse_object(oid->hash);
+ struct object *obj = parse_object(oid);
if (!obj || (obj->flags & ALTERNATE))
return;
@@ -118,9 +119,9 @@ static void rev_list_push(struct commit *commit, int mark)
}
}
-static int rev_list_insert_ref(const char *refname, const unsigned char *sha1)
+static int rev_list_insert_ref(const char *refname, const struct object_id *oid)
{
- struct object *o = deref_tag(parse_object(sha1), refname, 0);
+ struct object *o = deref_tag(parse_object(oid), refname, 0);
if (o && o->type == OBJ_COMMIT)
rev_list_push((struct commit *)o, SEEN);
@@ -131,13 +132,13 @@ static int rev_list_insert_ref(const char *refname, const unsigned char *sha1)
static int rev_list_insert_ref_oid(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
{
- return rev_list_insert_ref(refname, oid->hash);
+ return rev_list_insert_ref(refname, oid);
}
static int clear_marks(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
{
- struct object *o = deref_tag(parse_object(oid->hash), refname, 0);
+ struct object *o = deref_tag(parse_object(oid), refname, 0);
if (o && o->type == OBJ_COMMIT)
clear_commit_marks((struct commit *)o,
@@ -183,7 +184,7 @@ static void mark_common(struct commit *commit,
Get the next rev to send, ignoring the common.
*/
-static const unsigned char *get_rev(void)
+static const struct object_id *get_rev(void)
{
struct commit *commit = NULL;
@@ -222,7 +223,7 @@ static const unsigned char *get_rev(void)
}
}
- return commit->object.oid.hash;
+ return &commit->object.oid;
}
enum ack_type {
@@ -251,7 +252,7 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
}
}
-static enum ack_type get_ack(int fd, unsigned char *result_sha1)
+static enum ack_type get_ack(int fd, struct object_id *result_oid)
{
int len;
char *line = packet_read_line(fd, &len);
@@ -262,7 +263,7 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
if (!strcmp(line, "NAK"))
return NAK;
if (skip_prefix(line, "ACK ", &arg)) {
- if (!get_sha1_hex(arg, result_sha1)) {
+ if (!get_oid_hex(arg, result_oid)) {
arg += 40;
len -= arg - line;
if (len < 1)
@@ -293,7 +294,7 @@ static void send_request(struct fetch_pack_args *args,
static void insert_one_alternate_object(struct object *obj)
{
- rev_list_insert_ref(NULL, obj->oid.hash);
+ rev_list_insert_ref(NULL, &obj->oid);
}
#define INITIAL_FLUSH 16
@@ -317,12 +318,12 @@ static int next_flush(struct fetch_pack_args *args, int count)
}
static int find_common(struct fetch_pack_args *args,
- int fd[2], unsigned char *result_sha1,
+ int fd[2], struct object_id *result_oid,
struct ref *refs)
{
int fetching;
int count = 0, flushes = 0, flush_at = INITIAL_FLUSH, retval;
- const unsigned char *sha1;
+ const struct object_id *oid;
unsigned in_vain = 0;
int got_continue = 0;
int got_ready = 0;
@@ -340,7 +341,7 @@ static int find_common(struct fetch_pack_args *args,
fetching = 0;
for ( ; refs ; refs = refs->next) {
- unsigned char *remote = refs->old_oid.hash;
+ struct object_id *remote = &refs->old_oid;
const char *remote_hex;
struct object *o;
@@ -354,12 +355,12 @@ static int find_common(struct fetch_pack_args *args,
* interested in the case we *know* the object is
* reachable and we have already scanned it.
*/
- if (((o = lookup_object(remote)) != NULL) &&
+ if (((o = lookup_object(remote->hash)) != NULL) &&
(o->flags & COMPLETE)) {
continue;
}
- remote_hex = sha1_to_hex(remote);
+ remote_hex = oid_to_hex(remote);
if (!fetching) {
struct strbuf c = STRBUF_INIT;
if (multi_ack == 2) strbuf_addstr(&c, " multi_ack_detailed");
@@ -394,8 +395,8 @@ static int find_common(struct fetch_pack_args *args,
if (args->depth > 0)
packet_buf_write(&req_buf, "deepen %d", args->depth);
if (args->deepen_since) {
- unsigned long max_age = approxidate(args->deepen_since);
- packet_buf_write(&req_buf, "deepen-since %lu", max_age);
+ timestamp_t max_age = approxidate(args->deepen_since);
+ packet_buf_write(&req_buf, "deepen-since %"PRItime, max_age);
}
if (args->deepen_not) {
int i;
@@ -410,25 +411,25 @@ static int find_common(struct fetch_pack_args *args,
if (args->deepen) {
char *line;
const char *arg;
- unsigned char sha1[20];
+ struct object_id oid;
send_request(args, fd[1], &req_buf);
while ((line = packet_read_line(fd[0], NULL))) {
if (skip_prefix(line, "shallow ", &arg)) {
- if (get_sha1_hex(arg, sha1))
+ if (get_oid_hex(arg, &oid))
die(_("invalid shallow line: %s"), line);
- register_shallow(sha1);
+ register_shallow(&oid);
continue;
}
if (skip_prefix(line, "unshallow ", &arg)) {
- if (get_sha1_hex(arg, sha1))
+ if (get_oid_hex(arg, &oid))
die(_("invalid unshallow line: %s"), line);
- if (!lookup_object(sha1))
+ if (!lookup_object(oid.hash))
die(_("object not found: %s"), line);
/* make sure that it is parsed as shallow */
- if (!parse_object(sha1))
+ if (!parse_object(&oid))
die(_("error in object: %s"), line);
- if (unregister_shallow(sha1))
+ if (unregister_shallow(&oid))
die(_("no shallow found: %s"), line);
continue;
}
@@ -447,9 +448,9 @@ static int find_common(struct fetch_pack_args *args,
flushes = 0;
retval = -1;
- while ((sha1 = get_rev())) {
- packet_buf_write(&req_buf, "have %s\n", sha1_to_hex(sha1));
- print_verbose(args, "have %s", sha1_to_hex(sha1));
+ while ((oid = get_rev())) {
+ packet_buf_write(&req_buf, "have %s\n", oid_to_hex(oid));
+ print_verbose(args, "have %s", oid_to_hex(oid));
in_vain++;
if (flush_at <= ++count) {
int ack;
@@ -469,10 +470,10 @@ static int find_common(struct fetch_pack_args *args,
consume_shallow_list(args, fd[0]);
do {
- ack = get_ack(fd[0], result_sha1);
+ ack = get_ack(fd[0], result_oid);
if (ack)
print_verbose(args, _("got %s %d %s"), "ack",
- ack, sha1_to_hex(result_sha1));
+ ack, oid_to_hex(result_oid));
switch (ack) {
case ACK:
flushes = 0;
@@ -483,9 +484,9 @@ static int find_common(struct fetch_pack_args *args,
case ACK_ready:
case ACK_continue: {
struct commit *commit =
- lookup_commit(result_sha1);
+ lookup_commit(result_oid);
if (!commit)
- die(_("invalid commit %s"), sha1_to_hex(result_sha1));
+ die(_("invalid commit %s"), oid_to_hex(result_oid));
if (args->stateless_rpc
&& ack == ACK_common
&& !(commit->object.flags & COMMON)) {
@@ -493,7 +494,7 @@ static int find_common(struct fetch_pack_args *args,
* on the next RPC request so the peer knows
* it is in common with us.
*/
- const char *hex = sha1_to_hex(result_sha1);
+ const char *hex = oid_to_hex(result_oid);
packet_buf_write(&req_buf, "have %s\n", hex);
state_len = req_buf.len;
/*
@@ -538,10 +539,10 @@ done:
if (!got_ready || !no_done)
consume_shallow_list(args, fd[0]);
while (flushes || multi_ack) {
- int ack = get_ack(fd[0], result_sha1);
+ int ack = get_ack(fd[0], result_oid);
if (ack) {
print_verbose(args, _("got %s (%d) %s"), "ack",
- ack, sha1_to_hex(result_sha1));
+ ack, oid_to_hex(result_oid));
if (ack == ACK)
return 0;
multi_ack = 1;
@@ -555,16 +556,16 @@ done:
static struct commit_list *complete;
-static int mark_complete(const unsigned char *sha1)
+static int mark_complete(const struct object_id *oid)
{
- struct object *o = parse_object(sha1);
+ struct object *o = parse_object(oid);
while (o && o->type == OBJ_TAG) {
struct tag *t = (struct tag *) o;
if (!t->tagged)
break; /* broken repository */
o->flags |= COMPLETE;
- o = parse_object(t->tagged->oid.hash);
+ o = parse_object(&t->tagged->oid);
}
if (o && o->type == OBJ_COMMIT) {
struct commit *commit = (struct commit *)o;
@@ -579,11 +580,11 @@ static int mark_complete(const unsigned char *sha1)
static int mark_complete_oid(const char *refname, const struct object_id *oid,
int flag, void *cb_data)
{
- return mark_complete(oid->hash);
+ return mark_complete(oid);
}
static void mark_recent_complete_commits(struct fetch_pack_args *args,
- unsigned long cutoff)
+ timestamp_t cutoff)
{
while (complete && cutoff <= complete->item->date) {
print_verbose(args, _("Marking %s as complete"),
@@ -592,13 +593,38 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,
}
}
+static void add_refs_to_oidset(struct oidset *oids, struct ref *refs)
+{
+ for (; refs; refs = refs->next)
+ 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)
+{
+ /*
+ * 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.tablesize) {
+ add_refs_to_oidset(tip_oids, unmatched);
+ add_refs_to_oidset(tip_oids, newlist);
+ }
+ return oidset_contains(tip_oids, id);
+}
+
static void filter_refs(struct fetch_pack_args *args,
struct ref **refs,
struct ref **sought, int nr_sought)
{
struct ref *newlist = NULL;
struct ref **newtail = &newlist;
+ struct ref *unmatched = NULL;
struct ref *ref, *next;
+ struct oidset tip_oids = OIDSET_INIT;
int i;
i = 0;
@@ -631,24 +657,28 @@ static void filter_refs(struct fetch_pack_args *args,
ref->next = NULL;
newtail = &ref->next;
} else {
- free(ref);
+ ref->next = unmatched;
+ unmatched = ref;
}
}
/* Append unmatched requests to the list */
for (i = 0; i < nr_sought; i++) {
- unsigned char sha1[20];
+ struct object_id oid;
+ const char *p;
ref = sought[i];
if (ref->match_status != REF_NOT_MATCHED)
continue;
- if (get_sha1_hex(ref->name, sha1) ||
- ref->name[40] != '\0' ||
- hashcmp(sha1, ref->old_oid.hash))
+ if (parse_oid_hex(ref->name, &oid, &p) ||
+ *p != '\0' ||
+ oidcmp(&oid, &ref->old_oid))
continue;
if ((allow_unadvertised_object_request &
- (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
+ (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1)) ||
+ tip_oids_contain(&tip_oids, unmatched, newlist,
+ &ref->old_oid)) {
ref->match_status = REF_MATCHED;
*newtail = copy_ref(ref);
newtail = &(*newtail)->next;
@@ -656,12 +686,19 @@ static void filter_refs(struct fetch_pack_args *args,
ref->match_status = REF_UNADVERTISED_NOT_ALLOWED;
}
}
+
+ oidset_clear(&tip_oids);
+ for (ref = unmatched; ref; ref = next) {
+ next = ref->next;
+ free(ref);
+ }
+
*refs = newlist;
}
static void mark_alternate_complete(struct object *obj)
{
- mark_complete(obj->oid.hash);
+ mark_complete(&obj->oid);
}
static int everything_local(struct fetch_pack_args *args,
@@ -670,7 +707,7 @@ static int everything_local(struct fetch_pack_args *args,
{
struct ref *ref;
int retval;
- unsigned long cutoff = 0;
+ timestamp_t cutoff = 0;
save_commit_buffer = 0;
@@ -680,7 +717,7 @@ static int everything_local(struct fetch_pack_args *args,
if (!has_object_file(&ref->old_oid))
continue;
- o = parse_object(ref->old_oid.hash);
+ o = parse_object(&ref->old_oid);
if (!o)
continue;
@@ -724,17 +761,17 @@ static int everything_local(struct fetch_pack_args *args,
filter_refs(args, refs, sought, nr_sought);
for (retval = 1, ref = *refs; ref ; ref = ref->next) {
- const unsigned char *remote = ref->old_oid.hash;
+ const struct object_id *remote = &ref->old_oid;
struct object *o;
- o = lookup_object(remote);
+ o = lookup_object(remote->hash);
if (!o || !(o->flags & COMPLETE)) {
retval = 0;
- print_verbose(args, "want %s (%s)", sha1_to_hex(remote),
+ print_verbose(args, "want %s (%s)", oid_to_hex(remote),
ref->name);
continue;
}
- print_verbose(args, _("already have %s (%s)"), sha1_to_hex(remote),
+ print_verbose(args, _("already have %s (%s)"), oid_to_hex(remote),
ref->name);
}
return retval;
@@ -873,7 +910,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
char **pack_lockfile)
{
struct ref *ref = copy_ref_list(orig_ref);
- unsigned char sha1[20];
+ struct object_id oid;
const char *agent_feature;
int agent_len;
@@ -945,7 +982,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
packet_flush(fd[1]);
goto all_done;
}
- if (find_common(args, fd, sha1, ref) < 0)
+ if (find_common(args, fd, &oid, ref) < 0)
if (!args->keep_pack)
/* When cloning, it is not unusual to have
* no common commit.
diff --git a/fsck.c b/fsck.c
index e6152e4e6d..b4204d772b 100644
--- a/fsck.c
+++ b/fsck.c
@@ -358,14 +358,14 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
continue;
if (S_ISDIR(entry.mode)) {
- obj = &lookup_tree(entry.oid->hash)->object;
+ obj = &lookup_tree(entry.oid)->object;
if (name)
put_object_name(options, obj, "%s%s/", name,
entry.path);
result = options->walk(obj, OBJ_TREE, data, options);
}
else if (S_ISREG(entry.mode) || S_ISLNK(entry.mode)) {
- obj = &lookup_blob(entry.oid->hash)->object;
+ obj = &lookup_blob(entry.oid)->object;
if (name)
put_object_name(options, obj, "%s%s", name,
entry.path);
@@ -461,7 +461,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
return -1;
if (obj->type == OBJ_NONE)
- parse_object(obj->oid.hash);
+ parse_object(&obj->oid);
switch (obj->type) {
case OBJ_BLOB:
@@ -691,7 +691,7 @@ static int fsck_ident(const char **ident, struct object *obj, struct fsck_option
p++;
if (*p == '0' && p[1] != ' ')
return report(options, obj, FSCK_MSG_ZERO_PADDED_DATE, "invalid author/committer line - zero-padded date");
- if (date_overflows(strtoul(p, &end, 10)))
+ if (date_overflows(parse_timestamp(p, &end, 10)))
return report(options, obj, FSCK_MSG_BAD_DATE_OVERFLOW, "invalid author/committer line - date causes integer overflow");
if ((end == p || *end != ' '))
return report(options, obj, FSCK_MSG_BAD_DATE, "invalid author/committer line - bad date");
diff --git a/git-add--interactive.perl b/git-add--interactive.perl
index 709a5f6ce6..79d675b5b0 100755
--- a/git-add--interactive.perl
+++ b/git-add--interactive.perl
@@ -46,7 +46,6 @@ my ($diff_new_color) =
my $normal_color = $repo->get_color("", "reset");
my $diff_algorithm = $repo->config('diff.algorithm');
-my $diff_indent_heuristic = $repo->config_bool('diff.indentheuristic');
my $diff_filter = $repo->config('interactive.difffilter');
my $use_readkey = 0;
@@ -730,9 +729,6 @@ sub parse_diff {
if (defined $diff_algorithm) {
splice @diff_cmd, 1, 0, "--diff-algorithm=${diff_algorithm}";
}
- if ($diff_indent_heuristic) {
- splice @diff_cmd, 1, 0, "--indent-heuristic";
- }
if (defined $patch_mode_revision) {
push @diff_cmd, get_diff_reference($patch_mode_revision);
}
diff --git a/git-compat-util.h b/git-compat-util.h
index bd04564a69..4b7dcf21ad 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -319,6 +319,11 @@ extern char *gitdirname(char *);
#define PRIo32 "o"
#endif
+typedef uintmax_t timestamp_t;
+#define PRItime PRIuMAX
+#define parse_timestamp strtoumax
+#define TIME_MAX UINTMAX_MAX
+
#ifndef PATH_SEP
#define PATH_SEP ':'
#endif
@@ -445,7 +450,6 @@ extern void (*get_error_routine(void))(const char *err, va_list params);
extern void set_warn_routine(void (*routine)(const char *warn, va_list params));
extern void (*get_warn_routine(void))(const char *warn, va_list params);
extern void set_die_is_recursing_routine(int (*routine)(void));
-extern void set_error_handle(FILE *);
extern int starts_with(const char *str, const char *prefix);
@@ -616,7 +620,7 @@ extern int git_lstat(const char *, struct stat *);
#endif
#define DEFAULT_PACKED_GIT_LIMIT \
- ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? 8192 : 256))
+ ((1024L * 1024L) * (size_t)(sizeof(void*) >= 8 ? (32 * 1024L * 1024L) : 256))
#ifdef NO_PREAD
#define pread git_pread
@@ -1064,6 +1068,15 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,
#define HAVE_VARIADIC_MACROS 1
#endif
+#ifdef HAVE_VARIADIC_MACROS
+__attribute__((format (printf, 3, 4))) NORETURN
+void BUG_fl(const char *file, int line, const char *fmt, ...);
+#define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)
+#else
+__attribute__((format (printf, 1, 2))) NORETURN
+void BUG(const char *fmt, ...);
+#endif
+
/*
* Preserves errno, prints a message, but gives no warning for ENOENT.
* Returns 0 on success, which includes trying to unlink an object that does
diff --git a/git-send-email.perl b/git-send-email.perl
index eea0a517f7..7fd5874436 100755
--- a/git-send-email.perl
+++ b/git-send-email.perl
@@ -25,8 +25,9 @@ use Getopt::Long;
use Text::ParseWords;
use Term::ANSIColor;
use File::Temp qw/ tempdir tempfile /;
-use File::Spec::Functions qw(catfile);
+use File::Spec::Functions qw(catdir catfile);
use Error qw(:try);
+use Cwd qw(abs_path cwd);
use Git;
use Git::I18N;
@@ -1353,10 +1354,12 @@ EOF
die __("The required SMTP server is not properly defined.")
}
+ require Net::SMTP;
+ my $use_net_smtp_ssl = version->parse($Net::SMTP::VERSION) < version->parse("2.34");
+ $smtp_domain ||= maildomain();
+
if ($smtp_encryption eq 'ssl') {
$smtp_server_port ||= 465; # ssmtp
- require Net::SMTP::SSL;
- $smtp_domain ||= maildomain();
require IO::Socket::SSL;
# Suppress "variable accessed once" warning.
@@ -1368,34 +1371,48 @@ EOF
# Net::SMTP::SSL->new() does not forward any SSL options
IO::Socket::SSL::set_client_defaults(
ssl_verify_params());
- $smtp ||= Net::SMTP::SSL->new($smtp_server,
- Hello => $smtp_domain,
- Port => $smtp_server_port,
- Debug => $debug_net_smtp);
+
+ if ($use_net_smtp_ssl) {
+ require Net::SMTP::SSL;
+ $smtp ||= Net::SMTP::SSL->new($smtp_server,
+ Hello => $smtp_domain,
+ Port => $smtp_server_port,
+ Debug => $debug_net_smtp);
+ }
+ else {
+ $smtp ||= Net::SMTP->new($smtp_server,
+ Hello => $smtp_domain,
+ Port => $smtp_server_port,
+ Debug => $debug_net_smtp,
+ SSL => 1);
+ }
}
else {
- require Net::SMTP;
- $smtp_domain ||= maildomain();
$smtp_server_port ||= 25;
$smtp ||= Net::SMTP->new($smtp_server,
Hello => $smtp_domain,
Debug => $debug_net_smtp,
Port => $smtp_server_port);
if ($smtp_encryption eq 'tls' && $smtp) {
- require Net::SMTP::SSL;
- $smtp->command('STARTTLS');
- $smtp->response();
- if ($smtp->code == 220) {
+ if ($use_net_smtp_ssl) {
+ $smtp->command('STARTTLS');
+ $smtp->response();
+ if ($smtp->code != 220) {
+ die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message);
+ }
+ require Net::SMTP::SSL;
$smtp = Net::SMTP::SSL->start_SSL($smtp,
ssl_verify_params())
- or die "STARTTLS failed! ".IO::Socket::SSL::errstr();
- $smtp_encryption = '';
- # Send EHLO again to receive fresh
- # supported commands
- $smtp->hello($smtp_domain);
- } else {
- die sprintf(__("Server does not support STARTTLS! %s"), $smtp->message);
+ or die sprintf(__("STARTTLS failed! %s"), IO::Socket::SSL::errstr());
+ }
+ else {
+ $smtp->starttls(ssl_verify_params())
+ or die sprintf(__("STARTTLS failed! %s"), IO::Socket::SSL::errstr());
}
+ $smtp_encryption = '';
+ # Send EHLO again to receive fresh
+ # supported commands
+ $smtp->hello($smtp_domain);
}
}
@@ -1737,6 +1754,25 @@ sub unique_email_list {
sub validate_patch {
my $fn = shift;
+
+ if ($repo) {
+ my $validate_hook = catfile(catdir($repo->repo_path(), 'hooks'),
+ 'sendemail-validate');
+ my $hook_error;
+ if (-x $validate_hook) {
+ my $target = abs_path($fn);
+ # The hook needs a correct cwd and GIT_DIR.
+ my $cwd_save = cwd();
+ chdir($repo->wc_path() or $repo->repo_path())
+ or die("chdir: $!");
+ local $ENV{"GIT_DIR"} = $repo->repo_path();
+ $hook_error = "rejected by sendemail-validate hook"
+ if system($validate_hook, $target);
+ chdir($cwd_save) or die("chdir: $!");
+ }
+ return $hook_error if $hook_error;
+ }
+
open(my $fh, '<', $fn)
or die sprintf(__("unable to open %s: %s\n"), $fn, $!);
while (my $line = <$fh>) {
diff --git a/grep.c b/grep.c
index 47cee45067..d03d424e5c 100644
--- a/grep.c
+++ b/grep.c
@@ -178,26 +178,23 @@ static void grep_set_pattern_type_option(enum grep_pattern_type pattern_type, st
case GREP_PATTERN_TYPE_BRE:
opt->fixed = 0;
- opt->pcre = 0;
- opt->regflags &= ~REG_EXTENDED;
+ opt->pcre1 = 0;
break;
case GREP_PATTERN_TYPE_ERE:
opt->fixed = 0;
- opt->pcre = 0;
+ opt->pcre1 = 0;
opt->regflags |= REG_EXTENDED;
break;
case GREP_PATTERN_TYPE_FIXED:
opt->fixed = 1;
- opt->pcre = 0;
- opt->regflags &= ~REG_EXTENDED;
+ opt->pcre1 = 0;
break;
case GREP_PATTERN_TYPE_PCRE:
opt->fixed = 0;
- opt->pcre = 1;
- opt->regflags &= ~REG_EXTENDED;
+ opt->pcre1 = 1;
break;
}
}
@@ -324,8 +321,32 @@ static NORETURN void compile_regexp_failed(const struct grep_pat *p,
die("%s'%s': %s", where, p->pattern, error);
}
-#ifdef USE_LIBPCRE
-static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
+static int is_fixed(const char *s, size_t len)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++) {
+ if (is_regex_special(s[i]))
+ return 0;
+ }
+
+ return 1;
+}
+
+static int has_null(const char *s, size_t len)
+{
+ /*
+ * regcomp cannot accept patterns with NULs so when using it
+ * we consider any pattern containing a NUL fixed.
+ */
+ if (memchr(s, 0, len))
+ return 1;
+
+ return 0;
+}
+
+#ifdef USE_LIBPCRE1
+static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
{
const char *error;
int erroffset;
@@ -333,23 +354,23 @@ static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
if (opt->ignore_case) {
if (has_non_ascii(p->pattern))
- p->pcre_tables = pcre_maketables();
+ p->pcre1_tables = pcre_maketables();
options |= PCRE_CASELESS;
}
if (is_utf8_locale() && has_non_ascii(p->pattern))
options |= PCRE_UTF8;
- p->pcre_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
- p->pcre_tables);
- if (!p->pcre_regexp)
+ p->pcre1_regexp = pcre_compile(p->pattern, options, &error, &erroffset,
+ p->pcre1_tables);
+ if (!p->pcre1_regexp)
compile_regexp_failed(p, error);
- p->pcre_extra_info = pcre_study(p->pcre_regexp, 0, &error);
- if (!p->pcre_extra_info && error)
+ p->pcre1_extra_info = pcre_study(p->pcre1_regexp, 0, &error);
+ if (!p->pcre1_extra_info && error)
die("%s", error);
}
-static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
+static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
regmatch_t *match, int eflags)
{
int ovector[30], ret, flags = 0;
@@ -357,7 +378,7 @@ static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
if (eflags & REG_NOTBOL)
flags |= PCRE_NOTBOL;
- ret = pcre_exec(p->pcre_regexp, p->pcre_extra_info, line, eol - line,
+ ret = pcre_exec(p->pcre1_regexp, p->pcre1_extra_info, line, eol - line,
0, flags, ovector, ARRAY_SIZE(ovector));
if (ret < 0 && ret != PCRE_ERROR_NOMATCH)
die("pcre_exec failed with error code %d", ret);
@@ -370,55 +391,36 @@ static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
return ret;
}
-static void free_pcre_regexp(struct grep_pat *p)
+static void free_pcre1_regexp(struct grep_pat *p)
{
- pcre_free(p->pcre_regexp);
- pcre_free(p->pcre_extra_info);
- pcre_free((void *)p->pcre_tables);
+ pcre_free(p->pcre1_regexp);
+ pcre_free(p->pcre1_extra_info);
+ pcre_free((void *)p->pcre1_tables);
}
-#else /* !USE_LIBPCRE */
-static void compile_pcre_regexp(struct grep_pat *p, const struct grep_opt *opt)
+#else /* !USE_LIBPCRE1 */
+static void compile_pcre1_regexp(struct grep_pat *p, const struct grep_opt *opt)
{
die("cannot use Perl-compatible regexes when not compiled with USE_LIBPCRE");
}
-static int pcrematch(struct grep_pat *p, const char *line, const char *eol,
+static int pcre1match(struct grep_pat *p, const char *line, const char *eol,
regmatch_t *match, int eflags)
{
return 1;
}
-static void free_pcre_regexp(struct grep_pat *p)
+static void free_pcre1_regexp(struct grep_pat *p)
{
}
-#endif /* !USE_LIBPCRE */
-
-static int is_fixed(const char *s, size_t len)
-{
- size_t i;
-
- /* regcomp cannot accept patterns with NULs so we
- * consider any pattern containing a NUL fixed.
- */
- if (memchr(s, 0, len))
- return 1;
-
- for (i = 0; i < len; i++) {
- if (is_regex_special(s[i]))
- return 0;
- }
-
- return 1;
-}
+#endif /* !USE_LIBPCRE1 */
static void compile_fixed_regexp(struct grep_pat *p, struct grep_opt *opt)
{
struct strbuf sb = STRBUF_INIT;
int err;
- int regflags;
+ int regflags = opt->regflags;
basic_regex_quote_buf(&sb, p->pattern);
- regflags = opt->regflags & ~REG_EXTENDED;
if (opt->ignore_case)
regflags |= REG_ICASE;
err = regcomp(&p->regexp, sb.buf, regflags);
@@ -455,7 +457,9 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
* simple string match using kws. p->fixed tells us if we
* want to use kws.
*/
- if (opt->fixed || is_fixed(p->pattern, p->patternlen))
+ if (opt->fixed ||
+ has_null(p->pattern, p->patternlen) ||
+ is_fixed(p->pattern, p->patternlen))
p->fixed = !icase || ascii_only;
else
p->fixed = 0;
@@ -475,8 +479,8 @@ static void compile_regexp(struct grep_pat *p, struct grep_opt *opt)
return;
}
- if (opt->pcre) {
- compile_pcre_regexp(p, opt);
+ if (opt->pcre1) {
+ compile_pcre1_regexp(p, opt);
return;
}
@@ -832,8 +836,8 @@ void free_grep_patterns(struct grep_opt *opt)
case GREP_PATTERN_BODY:
if (p->kws)
kwsfree(p->kws);
- else if (p->pcre_regexp)
- free_pcre_regexp(p);
+ else if (p->pcre1_regexp)
+ free_pcre1_regexp(p);
else
regfree(&p->regexp);
free(p->pattern);
@@ -912,8 +916,8 @@ static int patmatch(struct grep_pat *p, char *line, char *eol,
if (p->fixed)
hit = !fixmatch(p, line, eol, match);
- else if (p->pcre_regexp)
- hit = !pcrematch(p, line, eol, match, eflags);
+ else if (p->pcre1_regexp)
+ hit = !pcre1match(p, line, eol, match, eflags);
else
hit = !regexec_buf(&p->regexp, line, eol - line, 1, match,
eflags);
diff --git a/grep.h b/grep.h
index 267534ca24..38ac82b638 100644
--- a/grep.h
+++ b/grep.h
@@ -1,7 +1,7 @@
#ifndef GREP_H
#define GREP_H
#include "color.h"
-#ifdef USE_LIBPCRE
+#ifdef USE_LIBPCRE1
#include <pcre.h>
#else
typedef int pcre;
@@ -46,9 +46,9 @@ struct grep_pat {
size_t patternlen;
enum grep_header_field field;
regex_t regexp;
- pcre *pcre_regexp;
- pcre_extra *pcre_extra_info;
- const unsigned char *pcre_tables;
+ pcre *pcre1_regexp;
+ pcre_extra *pcre1_extra_info;
+ const unsigned char *pcre1_tables;
kwset_t kws;
unsigned fixed:1;
unsigned ignore_case:1;
@@ -111,7 +111,7 @@ struct grep_opt {
int allow_textconv;
int extended;
int use_reflog_filter;
- int pcre;
+ int pcre1;
int relative;
int pathname;
int null_following_name;
diff --git a/help.c b/help.c
index a07f01e6f9..db7f3d79a0 100644
--- a/help.c
+++ b/help.c
@@ -1,6 +1,7 @@
#include "cache.h"
#include "builtin.h"
#include "exec_cmd.h"
+#include "run-command.h"
#include "levenshtein.h"
#include "help.h"
#include "common-cmds.h"
@@ -96,48 +97,6 @@ static void pretty_print_cmdnames(struct cmdnames *cmds, unsigned int colopts)
string_list_clear(&list, 0);
}
-static int is_executable(const char *name)
-{
- struct stat st;
-
- if (stat(name, &st) || /* stat, not lstat */
- !S_ISREG(st.st_mode))
- return 0;
-
-#if defined(GIT_WINDOWS_NATIVE)
- /*
- * On Windows there is no executable bit. The file extension
- * indicates whether it can be run as an executable, and Git
- * has special-handling to detect scripts and launch them
- * through the indicated script interpreter. We test for the
- * file extension first because virus scanners may make
- * it quite expensive to open many files.
- */
- if (ends_with(name, ".exe"))
- return S_IXUSR;
-
-{
- /*
- * Now that we know it does not have an executable extension,
- * peek into the file instead.
- */
- char buf[3] = { 0 };
- int n;
- int fd = open(name, O_RDONLY);
- st.st_mode &= ~S_IXUSR;
- if (fd >= 0) {
- n = read(fd, buf, 2);
- if (n == 2)
- /* look for a she-bang */
- if (!strcmp(buf, "#!"))
- st.st_mode |= S_IXUSR;
- close(fd);
- }
-}
-#endif
- return st.st_mode & S_IXUSR;
-}
-
static void list_commands_in_dir(struct cmdnames *cmds,
const char *path,
const char *prefix)
diff --git a/http-backend.c b/http-backend.c
index eef0a361f4..ba5ff1aa29 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -90,7 +90,7 @@ static void hdr_int(struct strbuf *hdr, const char *name, uintmax_t value)
strbuf_addf(hdr, "%s: %" PRIuMAX "\r\n", name, value);
}
-static void hdr_date(struct strbuf *hdr, const char *name, unsigned long when)
+static void hdr_date(struct strbuf *hdr, const char *name, timestamp_t when)
{
const char *value = show_date(when, 0, DATE_MODE(RFC2822));
hdr_str(hdr, name, value);
@@ -105,7 +105,7 @@ static void hdr_nocache(struct strbuf *hdr)
static void hdr_cache_forever(struct strbuf *hdr)
{
- unsigned long now = time(NULL);
+ timestamp_t now = time(NULL);
hdr_date(hdr, "Date", now);
hdr_date(hdr, "Expires", now + 31536000);
hdr_str(hdr, "Cache-Control", "public, max-age=31536000");
@@ -431,7 +431,7 @@ static int show_text_ref(const char *name, const struct object_id *oid,
{
const char *name_nons = strip_namespace(name);
struct strbuf *buf = cb_data;
- struct object *o = parse_object(oid->hash);
+ struct object *o = parse_object(oid);
if (!o)
return 0;
diff --git a/http-push.c b/http-push.c
index f0e3108f71..67c4d4b472 100644
--- a/http-push.c
+++ b/http-push.c
@@ -718,13 +718,13 @@ static int fetch_indices(void)
return ret;
}
-static void one_remote_object(const unsigned char *sha1)
+static void one_remote_object(const struct object_id *oid)
{
struct object *obj;
- obj = lookup_object(sha1);
+ obj = lookup_object(oid->hash);
if (!obj)
- obj = parse_object(sha1);
+ obj = parse_object(oid);
/* Ignore remote objects that don't exist locally */
if (!obj)
@@ -1013,26 +1013,26 @@ static void remote_ls(const char *path, int flags,
void *userData);
/* extract hex from sharded "xx/x{40}" filename */
-static int get_sha1_hex_from_objpath(const char *path, unsigned char *sha1)
+static int get_oid_hex_from_objpath(const char *path, struct object_id *oid)
{
- char hex[40];
+ char hex[GIT_MAX_HEXSZ];
- if (strlen(path) != 41)
+ if (strlen(path) != GIT_SHA1_HEXSZ + 1)
return -1;
memcpy(hex, path, 2);
path += 2;
path++; /* skip '/' */
- memcpy(hex, path, 38);
+ memcpy(hex, path, GIT_SHA1_HEXSZ - 2);
- return get_sha1_hex(hex, sha1);
+ return get_oid_hex(hex, oid);
}
static void process_ls_object(struct remote_ls_ctx *ls)
{
unsigned int *parent = (unsigned int *)ls->userData;
const char *path = ls->dentry_name;
- unsigned char sha1[20];
+ struct object_id oid;
if (!strcmp(ls->path, ls->dentry_name) && (ls->flags & IS_DIR)) {
remote_dir_exists[*parent] = 1;
@@ -1040,10 +1040,10 @@ static void process_ls_object(struct remote_ls_ctx *ls)
}
if (!skip_prefix(path, "objects/", &path) ||
- get_sha1_hex_from_objpath(path, sha1))
+ get_oid_hex_from_objpath(path, &oid))
return;
- one_remote_object(sha1);
+ one_remote_object(&oid);
}
static void process_ls_ref(struct remote_ls_ctx *ls)
@@ -1312,10 +1312,10 @@ static struct object_list **process_tree(struct tree *tree,
while (tree_entry(&desc, &entry))
switch (object_type(entry.mode)) {
case OBJ_TREE:
- p = process_tree(lookup_tree(entry.oid->hash), p);
+ p = process_tree(lookup_tree(entry.oid), p);
break;
case OBJ_BLOB:
- p = process_blob(lookup_blob(entry.oid->hash), p);
+ p = process_blob(lookup_blob(entry.oid), p);
break;
default:
/* Subproject commit - not in this repository */
@@ -1462,7 +1462,7 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
return;
}
- o = parse_object(ref->old_oid.hash);
+ o = parse_object(&ref->old_oid);
if (!o) {
fprintf(stderr,
"Unable to parse object %s for remote ref %s\n",
@@ -1536,7 +1536,7 @@ static int remote_exists(const char *path)
return ret;
}
-static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
+static void fetch_symref(const char *path, char **symref, struct object_id *oid)
{
char *url = xstrfmt("%s%s", repo->url, path);
struct strbuf buffer = STRBUF_INIT;
@@ -1549,7 +1549,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
free(*symref);
*symref = NULL;
- hashclr(sha1);
+ oidclr(oid);
if (buffer.len == 0)
return;
@@ -1561,16 +1561,17 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
if (skip_prefix(buffer.buf, "ref: ", &name)) {
*symref = xmemdupz(name, buffer.len - (name - buffer.buf));
} else {
- get_sha1_hex(buffer.buf, sha1);
+ get_oid_hex(buffer.buf, oid);
}
strbuf_release(&buffer);
}
-static int verify_merge_base(unsigned char *head_sha1, struct ref *remote)
+static int verify_merge_base(struct object_id *head_oid, struct ref *remote)
{
- struct commit *head = lookup_commit_or_die(head_sha1, "HEAD");
- struct commit *branch = lookup_commit_or_die(remote->old_oid.hash, remote->name);
+ struct commit *head = lookup_commit_or_die(head_oid, "HEAD");
+ struct commit *branch = lookup_commit_or_die(&remote->old_oid,
+ remote->name);
return in_merge_bases(branch, head);
}
@@ -1579,7 +1580,7 @@ static int delete_remote_branch(const char *pattern, int force)
{
struct ref *refs = remote_refs;
struct ref *remote_ref = NULL;
- unsigned char head_sha1[20];
+ struct object_id head_oid;
char *symref = NULL;
int match;
int patlen = strlen(pattern);
@@ -1610,7 +1611,7 @@ static int delete_remote_branch(const char *pattern, int force)
* Remote HEAD must be a symref (not exactly foolproof; a remote
* symlink to a symref will look like a symref)
*/
- fetch_symref("HEAD", &symref, head_sha1);
+ fetch_symref("HEAD", &symref, &head_oid);
if (!symref)
return error("Remote HEAD is not a symref");
@@ -1619,7 +1620,7 @@ static int delete_remote_branch(const char *pattern, int force)
if (!strcmp(remote_ref->name, symref))
return error("Remote branch %s is the current HEAD",
remote_ref->name);
- fetch_symref(symref, &symref, head_sha1);
+ fetch_symref(symref, &symref, &head_oid);
}
/* Run extra sanity checks if delete is not forced */
@@ -1627,10 +1628,10 @@ static int delete_remote_branch(const char *pattern, int force)
/* Remote HEAD must resolve to a known object */
if (symref)
return error("Remote HEAD symrefs too deep");
- if (is_null_sha1(head_sha1))
+ if (is_null_oid(&head_oid))
return error("Unable to resolve remote HEAD");
- if (!has_sha1_file(head_sha1))
- return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", sha1_to_hex(head_sha1));
+ if (!has_object_file(&head_oid))
+ return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", oid_to_hex(&head_oid));
/* Remote branch must resolve to a known object */
if (is_null_oid(&remote_ref->old_oid))
@@ -1640,7 +1641,7 @@ static int delete_remote_branch(const char *pattern, int force)
return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid));
/* Remote branch must be an ancestor of remote HEAD */
- if (!verify_merge_base(head_sha1, remote_ref)) {
+ if (!verify_merge_base(&head_oid, remote_ref)) {
return error("The branch '%s' is not an ancestor "
"of your current HEAD.\n"
"If you are sure you want to delete it,"
diff --git a/list-objects.c b/list-objects.c
index f3ca6aafb7..b3931fa434 100644
--- a/list-objects.c
+++ b/list-objects.c
@@ -110,7 +110,7 @@ static void process_tree(struct rev_info *revs,
if (S_ISDIR(entry.mode))
process_tree(revs,
- lookup_tree(entry.oid->hash),
+ lookup_tree(entry.oid),
show, base, entry.path,
cb_data);
else if (S_ISGITLINK(entry.mode))
@@ -119,7 +119,7 @@ static void process_tree(struct rev_info *revs,
cb_data);
else
process_blob(revs,
- lookup_blob(entry.oid->hash),
+ lookup_blob(entry.oid),
show, base, entry.path,
cb_data);
}
diff --git a/lockfile.h b/lockfile.h
index 7b715f9e77..572064939c 100644
--- a/lockfile.h
+++ b/lockfile.h
@@ -176,6 +176,14 @@ static inline int hold_lock_file_for_update(
}
/*
+ * Return a nonzero value iff `lk` is currently locked.
+ */
+static inline int is_lock_file_locked(struct lock_file *lk)
+{
+ return is_tempfile_active(&lk->tempfile);
+}
+
+/*
* Append an appropriate error message to `buf` following the failure
* of `hold_lock_file_for_update()` to lock `path`. `err` should be the
* `errno` set by the failing call.
diff --git a/log-tree.c b/log-tree.c
index 4618dd04ca..a4ec11c2bf 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -105,13 +105,13 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
warning("invalid replace ref %s", refname);
return 0;
}
- obj = parse_object(original_oid.hash);
+ obj = parse_object(&original_oid);
if (obj)
add_name_decoration(DECORATION_GRAFTED, "replaced", obj);
return 0;
}
- obj = parse_object(oid->hash);
+ obj = parse_object(oid);
if (!obj)
return 0;
@@ -132,7 +132,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
if (!obj)
break;
if (!obj->parsed)
- parse_object(obj->oid.hash);
+ parse_object(&obj->oid);
add_name_decoration(DECORATION_REF_TAG, refname, obj);
}
return 0;
@@ -140,7 +140,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
{
- struct commit *commit = lookup_commit(graft->oid.hash);
+ struct commit *commit = lookup_commit(&graft->oid);
if (!commit)
return 0;
add_name_decoration(DECORATION_GRAFTED, "grafted", &commit->object);
@@ -184,7 +184,7 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
{
const struct name_decoration *list, *head = NULL;
const char *branch_name = NULL;
- unsigned char unused[20];
+ struct object_id unused;
int rru_flags;
/* First find HEAD */
@@ -197,7 +197,7 @@ static const struct name_decoration *current_pointed_by_HEAD(const struct name_d
return NULL;
/* Now resolve and find the matching current branch */
- branch_name = resolve_ref_unsafe("HEAD", 0, unused, &rru_flags);
+ branch_name = resolve_ref_unsafe("HEAD", 0, unused.hash, &rru_flags);
if (!(rru_flags & REF_ISSYMREF))
return NULL;
@@ -456,13 +456,13 @@ static void show_signature(struct rev_info *opt, struct commit *commit)
strbuf_release(&signature);
}
-static int which_parent(const unsigned char *sha1, const struct commit *commit)
+static int which_parent(const struct object_id *oid, const struct commit *commit)
{
int nth;
const struct commit_list *parent;
for (nth = 0, parent = commit->parents; parent; parent = parent->next) {
- if (!hashcmp(parent->item->object.oid.hash, sha1))
+ if (!oidcmp(&parent->item->object.oid, oid))
return nth;
nth++;
}
@@ -481,14 +481,14 @@ static void show_one_mergetag(struct commit *commit,
void *data)
{
struct rev_info *opt = (struct rev_info *)data;
- unsigned char sha1[20];
+ struct object_id oid;
struct tag *tag;
struct strbuf verify_message;
int status, nth;
size_t payload_size, gpg_message_offset;
- hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), sha1);
- tag = lookup_tag(sha1);
+ hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), oid.hash);
+ tag = lookup_tag(&oid);
if (!tag)
return; /* error message already given */
@@ -500,7 +500,7 @@ static void show_one_mergetag(struct commit *commit,
&commit->parents->next->item->object.oid))
strbuf_addf(&verify_message,
"merged tag '%s'\n", tag->tag);
- else if ((nth = which_parent(tag->tagged->oid.hash, commit)) < 0)
+ else if ((nth = which_parent(&tag->tagged->oid, commit)) < 0)
strbuf_addf(&verify_message, "tag %s names a non-parent %s\n",
tag->tag, tag->tagged->oid.hash);
else
@@ -536,7 +536,7 @@ void show_log(struct rev_info *opt)
struct strbuf msgbuf = STRBUF_INIT;
struct log_info *log = opt->loginfo;
struct commit *commit = log->commit, *parent = log->parent;
- int abbrev_commit = opt->abbrev_commit ? opt->abbrev : 40;
+ int abbrev_commit = opt->abbrev_commit ? opt->abbrev : GIT_SHA1_HEXSZ;
const char *extra_headers = opt->extra_headers;
struct pretty_print_context ctx = {0};
diff --git a/merge-recursive.c b/merge-recursive.c
index 62decd51cc..ae5238d82c 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -67,7 +67,7 @@ static struct tree *shift_tree_object(struct tree *one, struct tree *two,
}
if (!oidcmp(&two->object.oid, &shifted))
return two;
- return lookup_tree(shifted.hash);
+ return lookup_tree(&shifted);
}
static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
@@ -304,7 +304,7 @@ struct tree *write_tree_from_memory(struct merge_options *o)
return NULL;
}
- result = lookup_tree(active_cache_tree->sha1);
+ result = lookup_tree(&active_cache_tree->oid);
return result;
}
@@ -994,11 +994,11 @@ static int merge_file_1(struct merge_options *o,
return ret;
result->clean = (merge_status == 0);
} else if (S_ISGITLINK(a->mode)) {
- result->clean = merge_submodule(result->oid.hash,
+ result->clean = merge_submodule(&result->oid,
one->path,
- one->oid.hash,
- a->oid.hash,
- b->oid.hash,
+ &one->oid,
+ &a->oid,
+ &b->oid,
!o->call_depth);
} else if (S_ISLNK(a->mode)) {
oidcpy(&result->oid, &a->oid);
@@ -2042,7 +2042,7 @@ int merge_recursive(struct merge_options *o,
/* if there is no common ancestor, use an empty tree */
struct tree *tree;
- tree = lookup_tree(EMPTY_TREE_SHA1_BIN);
+ tree = lookup_tree(&empty_tree_oid);
merged_common_ancestors = make_virtual_commit(tree, "ancestor");
}
@@ -2103,7 +2103,7 @@ static struct commit *get_ref(const struct object_id *oid, const char *name)
{
struct object *object;
- object = deref_tag(parse_object(oid->hash), name, strlen(name));
+ object = deref_tag(parse_object(oid), name, strlen(name));
if (!object)
return NULL;
if (object->type == OBJ_TREE)
diff --git a/merge.c b/merge.c
index 04ee5fc911..1d441ad942 100644
--- a/merge.c
+++ b/merge.c
@@ -44,8 +44,8 @@ int try_merge_command(const char *strategy, size_t xopts_nr,
return ret;
}
-int checkout_fast_forward(const unsigned char *head,
- const unsigned char *remote,
+int checkout_fast_forward(const struct object_id *head,
+ const struct object_id *remote,
int overwrite_ignore)
{
struct tree *trees[MAX_UNPACK_TREES];
diff --git a/notes-cache.c b/notes-cache.c
index 5dfc5cbd08..2843e98576 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -5,16 +5,16 @@
static int notes_cache_match_validity(const char *ref, const char *validity)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *commit;
struct pretty_print_context pretty_ctx;
struct strbuf msg = STRBUF_INIT;
int ret;
- if (read_ref(ref, sha1) < 0)
+ if (read_ref(ref, oid.hash) < 0)
return 0;
- commit = lookup_commit_reference_gently(sha1, 1);
+ commit = lookup_commit_reference_gently(&oid, 1);
if (!commit)
return 0;
@@ -46,8 +46,7 @@ void notes_cache_init(struct notes_cache *c, const char *name,
int notes_cache_write(struct notes_cache *c)
{
- unsigned char tree_sha1[20];
- unsigned char commit_sha1[20];
+ struct object_id tree_oid, commit_oid;
if (!c || !c->tree.initialized || !c->tree.update_ref ||
!*c->tree.update_ref)
@@ -55,19 +54,19 @@ int notes_cache_write(struct notes_cache *c)
if (!c->tree.dirty)
return 0;
- if (write_notes_tree(&c->tree, tree_sha1))
+ if (write_notes_tree(&c->tree, tree_oid.hash))
return -1;
- if (commit_tree(c->validity, strlen(c->validity), tree_sha1, NULL,
- commit_sha1, NULL, NULL) < 0)
+ if (commit_tree(c->validity, strlen(c->validity), tree_oid.hash, NULL,
+ commit_oid.hash, NULL, NULL) < 0)
return -1;
- if (update_ref("update notes cache", c->tree.update_ref, commit_sha1,
+ if (update_ref("update notes cache", c->tree.update_ref, commit_oid.hash,
NULL, 0, UPDATE_REFS_QUIET_ON_ERR) < 0)
return -1;
return 0;
}
-char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20],
+char *notes_cache_get(struct notes_cache *c, struct object_id *key_oid,
size_t *outsize)
{
const unsigned char *value_sha1;
@@ -75,7 +74,7 @@ char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20],
char *value;
unsigned long size;
- value_sha1 = get_note(&c->tree, key_sha1);
+ value_sha1 = get_note(&c->tree, key_oid->hash);
if (!value_sha1)
return NULL;
value = read_sha1_file(value_sha1, &type, &size);
@@ -84,12 +83,12 @@ char *notes_cache_get(struct notes_cache *c, unsigned char key_sha1[20],
return value;
}
-int notes_cache_put(struct notes_cache *c, unsigned char key_sha1[20],
+int notes_cache_put(struct notes_cache *c, struct object_id *key_oid,
const char *data, size_t size)
{
- unsigned char value_sha1[20];
+ struct object_id value_oid;
- if (write_sha1_file(data, size, "blob", value_sha1) < 0)
+ if (write_sha1_file(data, size, "blob", value_oid.hash) < 0)
return -1;
- return add_note(&c->tree, key_sha1, value_sha1, NULL);
+ return add_note(&c->tree, key_oid->hash, value_oid.hash, NULL);
}
diff --git a/notes-cache.h b/notes-cache.h
index 356f88fb3c..aeeee8409d 100644
--- a/notes-cache.h
+++ b/notes-cache.h
@@ -12,9 +12,9 @@ void notes_cache_init(struct notes_cache *c, const char *name,
const char *validity);
int notes_cache_write(struct notes_cache *c);
-char *notes_cache_get(struct notes_cache *c, unsigned char sha1[20], size_t
+char *notes_cache_get(struct notes_cache *c, struct object_id *oid, size_t
*outsize);
-int notes_cache_put(struct notes_cache *c, unsigned char sha1[20],
+int notes_cache_put(struct notes_cache *c, struct object_id *oid,
const char *data, size_t size);
#endif /* NOTES_CACHE_H */
diff --git a/notes-merge.c b/notes-merge.c
index 32caaaff74..6244f6af9c 100644
--- a/notes-merge.c
+++ b/notes-merge.c
@@ -535,7 +535,7 @@ int notes_merge(struct notes_merge_options *o,
struct notes_tree *local_tree,
unsigned char *result_sha1)
{
- unsigned char local_sha1[20], remote_sha1[20];
+ struct object_id local_oid, remote_oid;
struct commit *local, *remote;
struct commit_list *bases = NULL;
const unsigned char *base_sha1, *base_tree_sha1;
@@ -549,46 +549,46 @@ int notes_merge(struct notes_merge_options *o,
o->local_ref, o->remote_ref);
/* Dereference o->local_ref into local_sha1 */
- if (read_ref_full(o->local_ref, 0, local_sha1, NULL))
+ if (read_ref_full(o->local_ref, 0, local_oid.hash, NULL))
die("Failed to resolve local notes ref '%s'", o->local_ref);
else if (!check_refname_format(o->local_ref, 0) &&
- is_null_sha1(local_sha1))
+ is_null_oid(&local_oid))
local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */
- else if (!(local = lookup_commit_reference(local_sha1)))
+ else if (!(local = lookup_commit_reference(&local_oid)))
die("Could not parse local commit %s (%s)",
- sha1_to_hex(local_sha1), o->local_ref);
- trace_printf("\tlocal commit: %.7s\n", sha1_to_hex(local_sha1));
+ oid_to_hex(&local_oid), o->local_ref);
+ trace_printf("\tlocal commit: %.7s\n", oid_to_hex(&local_oid));
/* Dereference o->remote_ref into remote_sha1 */
- if (get_sha1(o->remote_ref, remote_sha1)) {
+ if (get_oid(o->remote_ref, &remote_oid)) {
/*
* Failed to get remote_sha1. If o->remote_ref looks like an
* unborn ref, perform the merge using an empty notes tree.
*/
if (!check_refname_format(o->remote_ref, 0)) {
- hashclr(remote_sha1);
+ oidclr(&remote_oid);
remote = NULL;
} else {
die("Failed to resolve remote notes ref '%s'",
o->remote_ref);
}
- } else if (!(remote = lookup_commit_reference(remote_sha1))) {
+ } else if (!(remote = lookup_commit_reference(&remote_oid))) {
die("Could not parse remote commit %s (%s)",
- sha1_to_hex(remote_sha1), o->remote_ref);
+ oid_to_hex(&remote_oid), o->remote_ref);
}
- trace_printf("\tremote commit: %.7s\n", sha1_to_hex(remote_sha1));
+ trace_printf("\tremote commit: %.7s\n", oid_to_hex(&remote_oid));
if (!local && !remote)
die("Cannot merge empty notes ref (%s) into empty notes ref "
"(%s)", o->remote_ref, o->local_ref);
if (!local) {
/* result == remote commit */
- hashcpy(result_sha1, remote_sha1);
+ hashcpy(result_sha1, remote_oid.hash);
goto found_result;
}
if (!remote) {
/* result == local commit */
- hashcpy(result_sha1, local_sha1);
+ hashcpy(result_sha1, local_oid.hash);
goto found_result;
}
assert(local && remote);
diff --git a/notes-utils.c b/notes-utils.c
index 8f9ad7d1f8..031503d7b2 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -7,18 +7,18 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
const char *msg, size_t msg_len,
unsigned char *result_sha1)
{
- unsigned char tree_sha1[20];
+ struct object_id tree_oid;
assert(t->initialized);
- if (write_notes_tree(t, tree_sha1))
+ if (write_notes_tree(t, tree_oid.hash))
die("Failed to write notes tree to database");
if (!parents) {
/* Deduce parent commit from t->ref */
- unsigned char parent_sha1[20];
- if (!read_ref(t->ref, parent_sha1)) {
- struct commit *parent = lookup_commit(parent_sha1);
+ struct object_id parent_oid;
+ if (!read_ref(t->ref, parent_oid.hash)) {
+ struct commit *parent = lookup_commit(&parent_oid);
if (parse_commit(parent))
die("Failed to find/parse commit %s", t->ref);
commit_list_insert(parent, &parents);
@@ -26,14 +26,14 @@ void create_notes_commit(struct notes_tree *t, struct commit_list *parents,
/* else: t->ref points to nothing, assume root/orphan commit */
}
- if (commit_tree(msg, msg_len, tree_sha1, parents, result_sha1, NULL, NULL))
+ if (commit_tree(msg, msg_len, tree_oid.hash, parents, result_sha1, NULL, NULL))
die("Failed to commit notes tree to database");
}
void commit_notes(struct notes_tree *t, const char *msg)
{
struct strbuf buf = STRBUF_INIT;
- unsigned char commit_sha1[20];
+ struct object_id commit_oid;
if (!t)
t = &default_notes_tree;
@@ -46,9 +46,9 @@ void commit_notes(struct notes_tree *t, const char *msg)
strbuf_addstr(&buf, msg);
strbuf_complete_line(&buf);
- create_notes_commit(t, NULL, buf.buf, buf.len, commit_sha1);
+ create_notes_commit(t, NULL, buf.buf, buf.len, commit_oid.hash);
strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
- update_ref(buf.buf, t->update_ref, commit_sha1, NULL, 0,
+ update_ref(buf.buf, t->update_ref, commit_oid.hash, NULL, 0,
UPDATE_REFS_DIE_ON_ERR);
strbuf_release(&buf);
diff --git a/object.c b/object.c
index e680d881a4..06ba3a11d8 100644
--- a/object.c
+++ b/object.c
@@ -180,21 +180,21 @@ struct object *lookup_unknown_object(const unsigned char *sha1)
return obj;
}
-struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
+struct object *parse_object_buffer(const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p)
{
struct object *obj;
*eaten_p = 0;
obj = NULL;
if (type == OBJ_BLOB) {
- struct blob *blob = lookup_blob(sha1);
+ struct blob *blob = lookup_blob(oid);
if (blob) {
if (parse_blob_buffer(blob, buffer, size))
return NULL;
obj = &blob->object;
}
} else if (type == OBJ_TREE) {
- struct tree *tree = lookup_tree(sha1);
+ struct tree *tree = lookup_tree(oid);
if (tree) {
obj = &tree->object;
if (!tree->buffer)
@@ -206,7 +206,7 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
}
}
} else if (type == OBJ_COMMIT) {
- struct commit *commit = lookup_commit(sha1);
+ struct commit *commit = lookup_commit(oid);
if (commit) {
if (parse_commit_buffer(commit, buffer, size))
return NULL;
@@ -217,54 +217,54 @@ struct object *parse_object_buffer(const unsigned char *sha1, enum object_type t
obj = &commit->object;
}
} else if (type == OBJ_TAG) {
- struct tag *tag = lookup_tag(sha1);
+ struct tag *tag = lookup_tag(oid);
if (tag) {
if (parse_tag_buffer(tag, buffer, size))
return NULL;
obj = &tag->object;
}
} else {
- warning("object %s has unknown type id %d", sha1_to_hex(sha1), type);
+ warning("object %s has unknown type id %d", oid_to_hex(oid), type);
obj = NULL;
}
return obj;
}
-struct object *parse_object_or_die(const unsigned char *sha1,
+struct object *parse_object_or_die(const struct object_id *oid,
const char *name)
{
- struct object *o = parse_object(sha1);
+ struct object *o = parse_object(oid);
if (o)
return o;
- die(_("unable to parse object: %s"), name ? name : sha1_to_hex(sha1));
+ die(_("unable to parse object: %s"), name ? name : oid_to_hex(oid));
}
-struct object *parse_object(const unsigned char *sha1)
+struct object *parse_object(const struct object_id *oid)
{
unsigned long size;
enum object_type type;
int eaten;
- const unsigned char *repl = lookup_replace_object(sha1);
+ const unsigned char *repl = lookup_replace_object(oid->hash);
void *buffer;
struct object *obj;
- obj = lookup_object(sha1);
+ obj = lookup_object(oid->hash);
if (obj && obj->parsed)
return obj;
if ((obj && obj->type == OBJ_BLOB) ||
- (!obj && has_sha1_file(sha1) &&
- sha1_object_info(sha1, NULL) == OBJ_BLOB)) {
+ (!obj && has_object_file(oid) &&
+ sha1_object_info(oid->hash, NULL) == OBJ_BLOB)) {
if (check_sha1_signature(repl, NULL, 0, NULL) < 0) {
- error("sha1 mismatch %s", sha1_to_hex(repl));
+ error("sha1 mismatch %s", oid_to_hex(oid));
return NULL;
}
- parse_blob_buffer(lookup_blob(sha1), NULL, 0);
- return lookup_object(sha1);
+ parse_blob_buffer(lookup_blob(oid), NULL, 0);
+ return lookup_object(oid->hash);
}
- buffer = read_sha1_file(sha1, &type, &size);
+ buffer = read_sha1_file(oid->hash, &type, &size);
if (buffer) {
if (check_sha1_signature(repl, buffer, size, typename(type)) < 0) {
free(buffer);
@@ -272,7 +272,7 @@ struct object *parse_object(const unsigned char *sha1)
return NULL;
}
- obj = parse_object_buffer(sha1, type, size, buffer, &eaten);
+ obj = parse_object_buffer(oid, type, size, buffer, &eaten);
if (!eaten)
free(buffer);
return obj;
diff --git a/object.h b/object.h
index f52957dcb3..33e5cc9943 100644
--- a/object.h
+++ b/object.h
@@ -89,20 +89,20 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet);
*
* Returns NULL if the object is missing or corrupt.
*/
-struct object *parse_object(const unsigned char *sha1);
+struct object *parse_object(const struct object_id *oid);
/*
* Like parse_object, but will die() instead of returning NULL. If the
* "name" parameter is not NULL, it is included in the error message
- * (otherwise, the sha1 hex is given).
+ * (otherwise, the hex object ID is given).
*/
-struct object *parse_object_or_die(const unsigned char *sha1, const char *name);
+struct object *parse_object_or_die(const struct object_id *oid, const char *name);
/* Given the result of read_sha1_file(), returns the object after
* parsing it. eaten_p indicates if the object has a borrowed copy
* of buffer and the caller should not free() it.
*/
-struct object *parse_object_buffer(const unsigned char *sha1, enum object_type type, unsigned long size, void *buffer, int *eaten_p);
+struct object *parse_object_buffer(const struct object_id *oid, enum object_type type, unsigned long size, void *buffer, int *eaten_p);
/** Returns the object, with potentially excess memory allocated. **/
struct object *lookup_unknown_object(const unsigned char *sha1);
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index e313f4f2bc..8e47a96b3b 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -73,7 +73,8 @@ void bitmap_writer_build_type_index(struct pack_idx_entry **index,
break;
default:
- real_type = sha1_object_info(entry->idx.sha1, NULL);
+ real_type = sha1_object_info(entry->idx.oid.hash,
+ NULL);
break;
}
@@ -96,7 +97,8 @@ void bitmap_writer_build_type_index(struct pack_idx_entry **index,
default:
die("Missing type information for %s (%d/%d)",
- sha1_to_hex(entry->idx.sha1), real_type, entry->type);
+ oid_to_hex(&entry->idx.oid), real_type,
+ entry->type);
}
}
}
@@ -459,7 +461,7 @@ static inline void dump_bitmap(struct sha1file *f, struct ewah_bitmap *bitmap)
static const unsigned char *sha1_access(size_t pos, void *table)
{
struct pack_idx_entry **index = table;
- return index[pos]->sha1;
+ return index[pos]->oid.hash;
}
static void write_selected_commits_v1(struct sha1file *f,
diff --git a/pack-bitmap.c b/pack-bitmap.c
index 39bcc16846..a3ac3dccd4 100644
--- a/pack-bitmap.c
+++ b/pack-bitmap.c
@@ -673,7 +673,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
struct object *object = pending_e[i].item;
if (object->type == OBJ_NONE)
- parse_object_or_die(object->oid.hash, NULL);
+ parse_object_or_die(&object->oid, NULL);
while (object->type == OBJ_TAG) {
struct tag *tag = (struct tag *) object;
@@ -685,7 +685,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
if (!tag->tagged)
die("bad tag");
- object = parse_object_or_die(tag->tagged->oid.hash, NULL);
+ object = parse_object_or_die(&tag->tagged->oid, NULL);
}
if (object->flags & UNINTERESTING)
diff --git a/pack-check.c b/pack-check.c
index 27f70d345f..e1fcb228fa 100644
--- a/pack-check.c
+++ b/pack-check.c
@@ -5,7 +5,10 @@
struct idx_entry {
off_t offset;
- const unsigned char *sha1;
+ union idx_entry_object {
+ const unsigned char *hash;
+ struct object_id *oid;
+ } oid;
unsigned int nr;
};
@@ -51,7 +54,7 @@ static int verify_packfile(struct packed_git *p,
off_t index_size = p->index_size;
const unsigned char *index_base = p->index_data;
git_SHA_CTX ctx;
- unsigned char sha1[20], *pack_sig;
+ unsigned char hash[GIT_MAX_RAWSZ], *pack_sig;
off_t offset = 0, pack_sig_ofs = 0;
uint32_t nr_objects, i;
int err = 0;
@@ -71,9 +74,9 @@ static int verify_packfile(struct packed_git *p,
remaining -= (unsigned int)(offset - pack_sig_ofs);
git_SHA1_Update(&ctx, in, remaining);
} while (offset < pack_sig_ofs);
- git_SHA1_Final(sha1, &ctx);
+ git_SHA1_Final(hash, &ctx);
pack_sig = use_pack(p, w_curs, pack_sig_ofs, NULL);
- if (hashcmp(sha1, pack_sig))
+ if (hashcmp(hash, pack_sig))
err = error("%s SHA1 checksum mismatch",
p->pack_name);
if (hashcmp(index_base + index_size - 40, pack_sig))
@@ -90,8 +93,8 @@ static int verify_packfile(struct packed_git *p,
entries[nr_objects].offset = pack_sig_ofs;
/* first sort entries by pack offset, since unpacking them is more efficient that way */
for (i = 0; i < nr_objects; i++) {
- entries[i].sha1 = nth_packed_object_sha1(p, i);
- if (!entries[i].sha1)
+ entries[i].oid.hash = nth_packed_object_sha1(p, i);
+ if (!entries[i].oid.hash)
die("internal error pack-check nth-packed-object");
entries[i].offset = nth_packed_object_offset(p, i);
entries[i].nr = i;
@@ -112,7 +115,7 @@ static int verify_packfile(struct packed_git *p,
if (check_pack_crc(p, w_curs, offset, len, nr))
err = error("index CRC mismatch for object %s "
"from %s at offset %"PRIuMAX"",
- sha1_to_hex(entries[i].sha1),
+ oid_to_hex(entries[i].oid.oid),
p->pack_name, (uintmax_t)offset);
}
@@ -135,14 +138,14 @@ static int verify_packfile(struct packed_git *p,
if (data_valid && !data)
err = error("cannot unpack %s from %s at offset %"PRIuMAX"",
- sha1_to_hex(entries[i].sha1), p->pack_name,
+ oid_to_hex(entries[i].oid.oid), p->pack_name,
(uintmax_t)entries[i].offset);
- else if (check_sha1_signature(entries[i].sha1, data, size, typename(type)))
+ else if (check_sha1_signature(entries[i].oid.hash, data, size, typename(type)))
err = error("packed %s from %s is corrupt",
- sha1_to_hex(entries[i].sha1), p->pack_name);
+ oid_to_hex(entries[i].oid.oid), p->pack_name);
else if (fn) {
int eaten = 0;
- err |= fn(entries[i].sha1, type, size, data, &eaten);
+ err |= fn(entries[i].oid.oid, type, size, data, &eaten);
if (eaten)
data = NULL;
}
diff --git a/pack-objects.c b/pack-objects.c
index 6398a8aa96..9558d13834 100644
--- a/pack-objects.c
+++ b/pack-objects.c
@@ -14,7 +14,7 @@ static uint32_t locate_object_entry_hash(struct packing_data *pdata,
while (pdata->index[i] > 0) {
uint32_t pos = pdata->index[i] - 1;
- if (!hashcmp(sha1, pdata->objects[pos].idx.sha1)) {
+ if (!hashcmp(sha1, pdata->objects[pos].idx.oid.hash)) {
*found = 1;
return i;
}
@@ -53,7 +53,9 @@ static void rehash_objects(struct packing_data *pdata)
for (i = 0; i < pdata->nr_objects; i++) {
int found;
- uint32_t ix = locate_object_entry_hash(pdata, entry->idx.sha1, &found);
+ uint32_t ix = locate_object_entry_hash(pdata,
+ entry->idx.oid.hash,
+ &found);
if (found)
die("BUG: Duplicate object in hash");
@@ -98,7 +100,7 @@ struct object_entry *packlist_alloc(struct packing_data *pdata,
new_entry = pdata->objects + pdata->nr_objects++;
memset(new_entry, 0, sizeof(*new_entry));
- hashcpy(new_entry->idx.sha1, sha1);
+ hashcpy(new_entry->idx.oid.hash, sha1);
if (pdata->index_size * 3 <= pdata->nr_objects * 4)
rehash_objects(pdata);
diff --git a/pack-write.c b/pack-write.c
index fa97b72559..a333ec6754 100644
--- a/pack-write.c
+++ b/pack-write.c
@@ -13,7 +13,7 @@ static int sha1_compare(const void *_a, const void *_b)
{
struct pack_idx_entry *a = *(struct pack_idx_entry **)_a;
struct pack_idx_entry *b = *(struct pack_idx_entry **)_b;
- return hashcmp(a->sha1, b->sha1);
+ return oidcmp(&a->oid, &b->oid);
}
static int cmp_uint32(const void *a_, const void *b_)
@@ -103,7 +103,7 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
struct pack_idx_entry **next = list;
while (next < last) {
struct pack_idx_entry *obj = *next;
- if (obj->sha1[0] != i)
+ if (obj->oid.hash[0] != i)
break;
next++;
}
@@ -122,11 +122,11 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
uint32_t offset = htonl(obj->offset);
sha1write(f, &offset, 4);
}
- sha1write(f, obj->sha1, 20);
+ sha1write(f, obj->oid.hash, 20);
if ((opts->flags & WRITE_IDX_STRICT) &&
- (i && !hashcmp(list[-2]->sha1, obj->sha1)))
+ (i && !oidcmp(&list[-2]->oid, &obj->oid)))
die("The same object %s appears twice in the pack",
- sha1_to_hex(obj->sha1));
+ oid_to_hex(&obj->oid));
}
if (index_version >= 2) {
diff --git a/pack.h b/pack.h
index 5c2158746e..8294341af1 100644
--- a/pack.h
+++ b/pack.h
@@ -67,7 +67,7 @@ struct pack_idx_header {
* Common part of object structure used for write_idx_file
*/
struct pack_idx_entry {
- unsigned char sha1[20];
+ struct object_id oid;
uint32_t crc32;
off_t offset;
};
@@ -75,7 +75,7 @@ struct pack_idx_entry {
struct progress;
/* Note, the data argument could be NULL if object type is blob */
-typedef int (*verify_fn)(const unsigned char*, enum object_type, unsigned long, void*, int*);
+typedef int (*verify_fn)(const struct object_id *, enum object_type, unsigned long, void*, int*);
extern const char *write_idx_file(const char *index_name, struct pack_idx_entry **objects, int nr_objects, const struct pack_idx_option *, const unsigned char *sha1);
extern int check_pack_crc(struct packed_git *p, struct pack_window **w_curs, off_t offset, off_t len, unsigned int nr);
diff --git a/parse-options-cb.c b/parse-options-cb.c
index 7419780a9b..c6679cb2cd 100644
--- a/parse-options-cb.c
+++ b/parse-options-cb.c
@@ -31,14 +31,14 @@ int parse_opt_abbrev_cb(const struct option *opt, const char *arg, int unset)
int parse_opt_approxidate_cb(const struct option *opt, const char *arg,
int unset)
{
- *(unsigned long *)(opt->value) = approxidate(arg);
+ *(timestamp_t *)(opt->value) = approxidate(arg);
return 0;
}
int parse_opt_expiry_date_cb(const struct option *opt, const char *arg,
int unset)
{
- return parse_expiry_date(arg, (unsigned long *)opt->value);
+ return parse_expiry_date(arg, (timestamp_t *)opt->value);
}
int parse_opt_color_flag_cb(const struct option *opt, const char *arg,
@@ -80,14 +80,14 @@ int parse_opt_verbosity_cb(const struct option *opt, const char *arg,
int parse_opt_commits(const struct option *opt, const char *arg, int unset)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *commit;
if (!arg)
return -1;
- if (get_sha1(arg, sha1))
+ if (get_oid(arg, &oid))
return error("malformed object name %s", arg);
- commit = lookup_commit_reference(sha1);
+ commit = lookup_commit_reference(&oid);
if (!commit)
return error("no such commit %s", arg);
commit_list_insert(commit, opt->value);
diff --git a/pathspec.c b/pathspec.c
index 50f76fff45..828405021f 100644
--- a/pathspec.c
+++ b/pathspec.c
@@ -1,3 +1,4 @@
+#define NO_THE_INDEX_COMPATIBILITY_MACROS
#include "cache.h"
#include "dir.h"
#include "pathspec.h"
@@ -17,6 +18,7 @@
* to use find_pathspecs_matching_against_index() instead.
*/
void add_pathspec_matches_against_index(const struct pathspec *pathspec,
+ const struct index_state *istate,
char *seen)
{
int num_unmatched = 0, i;
@@ -32,8 +34,8 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec,
num_unmatched++;
if (!num_unmatched)
return;
- for (i = 0; i < active_nr; i++) {
- const struct cache_entry *ce = active_cache[i];
+ for (i = 0; i < istate->cache_nr; i++) {
+ const struct cache_entry *ce = istate->cache[i];
ce_path_match(ce, pathspec, seen);
}
}
@@ -46,10 +48,11 @@ void add_pathspec_matches_against_index(const struct pathspec *pathspec,
* nature of the "closest" (i.e. most specific) matches which each of the
* given pathspecs achieves against all items in the index.
*/
-char *find_pathspecs_matching_against_index(const struct pathspec *pathspec)
+char *find_pathspecs_matching_against_index(const struct pathspec *pathspec,
+ const struct index_state *istate)
{
char *seen = xcalloc(pathspec->nr, 1);
- add_pathspec_matches_against_index(pathspec, seen);
+ add_pathspec_matches_against_index(pathspec, istate, seen);
return seen;
}
@@ -386,65 +389,6 @@ static const char *parse_element_magic(unsigned *magic, int *prefix_len,
return parse_short_magic(magic, elem);
}
-static void strip_submodule_slash_cheap(struct pathspec_item *item)
-{
- if (item->len >= 1 && item->match[item->len - 1] == '/') {
- int i = cache_name_pos(item->match, item->len - 1);
-
- if (i >= 0 && S_ISGITLINK(active_cache[i]->ce_mode)) {
- item->len--;
- item->match[item->len] = '\0';
- }
- }
-}
-
-static void strip_submodule_slash_expensive(struct pathspec_item *item)
-{
- int i;
-
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- int ce_len = ce_namelen(ce);
-
- if (!S_ISGITLINK(ce->ce_mode))
- continue;
-
- if (item->len <= ce_len || item->match[ce_len] != '/' ||
- memcmp(ce->name, item->match, ce_len))
- continue;
-
- if (item->len == ce_len + 1) {
- /* strip trailing slash */
- item->len--;
- item->match[item->len] = '\0';
- } else {
- die(_("Pathspec '%s' is in submodule '%.*s'"),
- item->original, ce_len, ce->name);
- }
- }
-}
-
-static void die_inside_submodule_path(struct pathspec_item *item)
-{
- int i;
-
- for (i = 0; i < active_nr; i++) {
- struct cache_entry *ce = active_cache[i];
- int ce_len = ce_namelen(ce);
-
- if (!S_ISGITLINK(ce->ce_mode))
- continue;
-
- if (item->len < ce_len ||
- !(item->match[ce_len] == '/' || item->match[ce_len] == '\0') ||
- memcmp(ce->name, item->match, ce_len))
- continue;
-
- die(_("Pathspec '%s' is in submodule '%.*s'"),
- item->original, ce_len, ce->name);
- }
-}
-
/*
* Perform the initialization of a pathspec_item based on a pathspec element.
*/
@@ -517,12 +461,6 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
item->original = xstrdup(elt);
}
- if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP)
- strip_submodule_slash_cheap(item);
-
- if (flags & PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE)
- strip_submodule_slash_expensive(item);
-
if (magic & PATHSPEC_LITERAL) {
item->nowildcard_len = item->len;
} else {
@@ -547,15 +485,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
/* sanity checks, pathspec matchers assume these are sane */
if (item->nowildcard_len > item->len ||
item->prefix > item->len) {
- /*
- * This case can be triggered by the user pointing us to a
- * pathspec inside a submodule, which is an input error.
- * Detect that here and complain, but fallback in the
- * non-submodule case to a BUG, as we have no idea what
- * would trigger that.
- */
- die_inside_submodule_path(item);
- die ("BUG: item->nowildcard_len > item->len || item->prefix > item->len)");
+ die ("BUG: error initializing pathspec_item");
}
}
diff --git a/pathspec.h b/pathspec.h
index 55e976972c..60e6500401 100644
--- a/pathspec.h
+++ b/pathspec.h
@@ -58,27 +58,17 @@ struct pathspec {
#define PATHSPEC_PREFER_CWD (1<<0) /* No args means match cwd */
#define PATHSPEC_PREFER_FULL (1<<1) /* No args means match everything */
#define PATHSPEC_MAXDEPTH_VALID (1<<2) /* max_depth field is valid */
-/* strip the trailing slash if the given path is a gitlink */
-#define PATHSPEC_STRIP_SUBMODULE_SLASH_CHEAP (1<<3)
/* die if a symlink is part of the given path's directory */
-#define PATHSPEC_SYMLINK_LEADING_PATH (1<<4)
-/*
- * This is like a combination of ..LEADING_PATH and .._SLASH_CHEAP
- * (but not the same): it strips the trailing slash if the given path
- * is a gitlink but also checks and dies if gitlink is part of the
- * leading path (i.e. the given path goes beyond a submodule). It's
- * safer than _SLASH_CHEAP and also more expensive.
- */
-#define PATHSPEC_STRIP_SUBMODULE_SLASH_EXPENSIVE (1<<5)
-#define PATHSPEC_PREFIX_ORIGIN (1<<6)
-#define PATHSPEC_KEEP_ORDER (1<<7)
+#define PATHSPEC_SYMLINK_LEADING_PATH (1<<3)
+#define PATHSPEC_PREFIX_ORIGIN (1<<4)
+#define PATHSPEC_KEEP_ORDER (1<<5)
/*
* For the callers that just need pure paths from somewhere else, not
* from command line. Global --*-pathspecs options are ignored. No
* magic is parsed in each pathspec either. If PATHSPEC_LITERAL is
* allowed, then it will automatically set for every pathspec.
*/
-#define PATHSPEC_LITERAL_PATH (1<<8)
+#define PATHSPEC_LITERAL_PATH (1<<6)
extern void parse_pathspec(struct pathspec *pathspec,
unsigned magic_mask,
@@ -106,7 +96,10 @@ static inline int ps_strcmp(const struct pathspec_item *item,
return strcmp(s1, s2);
}
-extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec);
-extern void add_pathspec_matches_against_index(const struct pathspec *pathspec, char *seen);
+extern void add_pathspec_matches_against_index(const struct pathspec *pathspec,
+ const struct index_state *istate,
+ char *seen);
+extern char *find_pathspecs_matching_against_index(const struct pathspec *pathspec,
+ const struct index_state *istate);
#endif /* PATHSPEC_H */
diff --git a/pkt-line.c b/pkt-line.c
index d4b6bfe076..9d845ecc3c 100644
--- a/pkt-line.c
+++ b/pkt-line.c
@@ -171,6 +171,25 @@ int packet_write_fmt_gently(int fd, const char *fmt, ...)
return status;
}
+int packet_writel(int fd, const char *line, ...)
+{
+ va_list args;
+ int err;
+ va_start(args, line);
+ for (;;) {
+ if (!line)
+ break;
+ if (strlen(line) > LARGE_PACKET_DATA_MAX)
+ return -1;
+ err = packet_write_fmt_gently(fd, "%s\n", line);
+ if (err)
+ return err;
+ line = va_arg(args, const char*);
+ }
+ va_end(args);
+ return packet_flush_gently(fd);
+}
+
static int packet_write_gently(const int fd_out, const char *buf, size_t size)
{
static char packet_write_buffer[LARGE_PACKET_MAX];
@@ -315,7 +334,7 @@ static char *packet_read_line_generic(int fd,
PACKET_READ_CHOMP_NEWLINE);
if (dst_len)
*dst_len = len;
- return len ? packet_buffer : NULL;
+ return (len > 0) ? packet_buffer : NULL;
}
char *packet_read_line(int fd, int *len_p)
@@ -323,6 +342,18 @@ char *packet_read_line(int fd, int *len_p)
return packet_read_line_generic(fd, NULL, NULL, len_p);
}
+int packet_read_line_gently(int fd, int *dst_len, char **dst_line)
+{
+ int len = packet_read(fd, NULL, NULL,
+ packet_buffer, sizeof(packet_buffer),
+ PACKET_READ_CHOMP_NEWLINE|PACKET_READ_GENTLE_ON_EOF);
+ if (dst_len)
+ *dst_len = len;
+ if (dst_line)
+ *dst_line = (len > 0) ? packet_buffer : NULL;
+ return len;
+}
+
char *packet_read_line_buf(char **src, size_t *src_len, int *dst_len)
{
return packet_read_line_generic(-1, src, src_len, dst_len);
diff --git a/pkt-line.h b/pkt-line.h
index 18eac64830..450183b649 100644
--- a/pkt-line.h
+++ b/pkt-line.h
@@ -25,6 +25,8 @@ void packet_buf_flush(struct strbuf *buf);
void packet_buf_write(struct strbuf *buf, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
int packet_flush_gently(int fd);
int packet_write_fmt_gently(int fd, const char *fmt, ...) __attribute__((format (printf, 2, 3)));
+LAST_ARG_MUST_BE_NULL
+int packet_writel(int fd, const char *line, ...);
int write_packetized_from_fd(int fd_in, int fd_out);
int write_packetized_from_buf(const char *src_in, size_t len, int fd_out);
@@ -74,6 +76,17 @@ int packet_read(int fd, char **src_buffer, size_t *src_len, char
char *packet_read_line(int fd, int *size);
/*
+ * Convenience wrapper for packet_read that sets the PACKET_READ_GENTLE_ON_EOF
+ * and CHOMP_NEWLINE options. The return value specifies the number of bytes
+ * read into the buffer or -1 on truncated input. If the *dst_line parameter
+ * is not NULL it will return NULL for a flush packet or when the number of
+ * bytes copied is zero and otherwise points to a static buffer (that may be
+ * overwritten by subsequent calls). If the size parameter is not NULL, the
+ * length of the packet is written to it.
+ */
+int packet_read_line_gently(int fd, int *size, char **dst_line);
+
+/*
* Same as packet_read_line, but read from a buf rather than a descriptor;
* see packet_read for details on how src_* is used.
*/
diff --git a/pretty.c b/pretty.c
index d0f86f5d85..09701bd2ff 100644
--- a/pretty.c
+++ b/pretty.c
@@ -405,11 +405,11 @@ static void add_rfc2047(struct strbuf *sb, const char *line, size_t len,
const char *show_ident_date(const struct ident_split *ident,
const struct date_mode *mode)
{
- unsigned long date = 0;
+ timestamp_t date = 0;
long tz = 0;
if (ident->date_begin && ident->date_end)
- date = strtoul(ident->date_begin, NULL, 10);
+ date = parse_timestamp(ident->date_begin, NULL, 10);
if (date_overflows(date))
date = 0;
else {
@@ -1137,7 +1137,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
/* these depend on the commit */
if (!commit->object.parsed)
- parse_object(commit->object.oid.hash);
+ parse_object(&commit->object.oid);
switch (placeholder[0]) {
case 'H': /* commit hash */
diff --git a/reachable.c b/reachable.c
index a8a979bd4f..c62efbfd43 100644
--- a/reachable.c
+++ b/reachable.c
@@ -33,7 +33,7 @@ static int add_one_ref(const char *path, const struct object_id *oid,
return 0;
}
- object = parse_object_or_die(oid->hash, path);
+ object = parse_object_or_die(oid, path);
add_pending_object(revs, object, "");
return 0;
@@ -55,11 +55,11 @@ static void mark_commit(struct commit *c, void *data)
struct recent_data {
struct rev_info *revs;
- unsigned long timestamp;
+ timestamp_t timestamp;
};
static void add_recent_object(const struct object_id *oid,
- unsigned long mtime,
+ timestamp_t mtime,
struct recent_data *data)
{
struct object *obj;
@@ -82,13 +82,13 @@ static void add_recent_object(const struct object_id *oid,
switch (type) {
case OBJ_TAG:
case OBJ_COMMIT:
- obj = parse_object_or_die(oid->hash, NULL);
+ obj = parse_object_or_die(oid, NULL);
break;
case OBJ_TREE:
- obj = (struct object *)lookup_tree(oid->hash);
+ obj = (struct object *)lookup_tree(oid);
break;
case OBJ_BLOB:
- obj = (struct object *)lookup_blob(oid->hash);
+ obj = (struct object *)lookup_blob(oid);
break;
default:
die("unknown object type for %s: %s",
@@ -139,7 +139,7 @@ static int add_recent_packed(const struct object_id *oid,
}
int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
- unsigned long timestamp)
+ timestamp_t timestamp)
{
struct recent_data data;
int r;
@@ -156,8 +156,7 @@ int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
}
void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
- unsigned long mark_recent,
- struct progress *progress)
+ timestamp_t mark_recent, struct progress *progress)
{
struct connectivity_progress cp;
diff --git a/reachable.h b/reachable.h
index d23efc36ec..3c00fa0526 100644
--- a/reachable.h
+++ b/reachable.h
@@ -3,8 +3,8 @@
struct progress;
extern int add_unseen_recent_objects_to_traversal(struct rev_info *revs,
- unsigned long timestamp);
+ timestamp_t timestamp);
extern void mark_reachable_objects(struct rev_info *revs, int mark_reflog,
- unsigned long mark_recent, struct progress *);
+ timestamp_t mark_recent, struct progress *);
#endif
diff --git a/read-cache.c b/read-cache.c
index 0d0081a11b..bc156a133e 100644
--- a/read-cache.c
+++ b/read-cache.c
@@ -1877,15 +1877,9 @@ int discard_index(struct index_state *istate)
{
int i;
- for (i = 0; i < istate->cache_nr; i++) {
- if (istate->cache[i]->index &&
- istate->split_index &&
- istate->split_index->base &&
- istate->cache[i]->index <= istate->split_index->base->cache_nr &&
- istate->cache[i] == istate->split_index->base->cache[istate->cache[i]->index - 1])
- continue;
+ unshare_split_index(istate, 1);
+ for (i = 0; i < istate->cache_nr; i++)
free(istate->cache[i]);
- }
resolve_undo_clear_index(istate);
istate->cache_nr = 0;
istate->cache_changed = 0;
@@ -2187,9 +2181,10 @@ void update_index_if_able(struct index_state *istate, struct lock_file *lockfile
rollback_lock_file(lockfile);
}
-static int do_write_index(struct index_state *istate, int newfd,
+static int do_write_index(struct index_state *istate, struct tempfile *tempfile,
int strip_extensions)
{
+ int newfd = tempfile->fd;
git_SHA_CTX c;
struct cache_header hdr;
int i, err, removed, extended, hdr_version;
@@ -2197,6 +2192,7 @@ static int do_write_index(struct index_state *istate, int newfd,
int entries = istate->cache_nr;
struct stat st;
struct strbuf previous_name_buf = STRBUF_INIT, *previous_name;
+ int drop_cache_tree = 0;
for (i = removed = extended = 0; i < entries; i++) {
if (cache[i]->ce_flags & CE_REMOVE)
@@ -2247,6 +2243,8 @@ static int do_write_index(struct index_state *istate, int newfd,
warning(msg, ce->name);
else
return error(msg, ce->name);
+
+ drop_cache_tree = 1;
}
if (ce_write_entry(&c, newfd, ce, previous_name) < 0)
return -1;
@@ -2265,7 +2263,7 @@ static int do_write_index(struct index_state *istate, int newfd,
if (err)
return -1;
}
- if (!strip_extensions && istate->cache_tree) {
+ if (!strip_extensions && !drop_cache_tree && istate->cache_tree) {
struct strbuf sb = STRBUF_INIT;
cache_tree_write(&sb, istate->cache_tree);
@@ -2298,7 +2296,11 @@ static int do_write_index(struct index_state *istate, int newfd,
return -1;
}
- if (ce_flush(&c, newfd, istate->sha1) || fstat(newfd, &st))
+ if (ce_flush(&c, newfd, istate->sha1))
+ return -1;
+ if (close_tempfile(tempfile))
+ return error(_("could not close '%s'"), tempfile->filename.buf);
+ if (stat(tempfile->filename.buf, &st))
return -1;
istate->timestamp.sec = (unsigned int)st.st_mtime;
istate->timestamp.nsec = ST_MTIME_NSEC(st);
@@ -2321,7 +2323,7 @@ static int commit_locked_index(struct lock_file *lk)
static int do_write_locked_index(struct index_state *istate, struct lock_file *lock,
unsigned flags)
{
- int ret = do_write_index(istate, get_lock_file_fd(lock), 0);
+ int ret = do_write_index(istate, &lock->tempfile, 0);
if (ret)
return ret;
assert((flags & (COMMIT_LOCK | CLOSE_LOCK)) !=
@@ -2418,7 +2420,7 @@ static int write_shared_index(struct index_state *istate,
return do_write_locked_index(istate, lock, flags);
}
move_cache_to_base_index(istate);
- ret = do_write_index(si->base, fd, 1);
+ ret = do_write_index(si->base, &temporary_sharedindex, 1);
if (ret) {
delete_tempfile(&temporary_sharedindex);
return ret;
@@ -2628,3 +2630,9 @@ void stat_validity_update(struct stat_validity *sv, int fd)
fill_stat_data(sv->sd, &st);
}
}
+
+void move_index_extensions(struct index_state *dst, struct index_state *src)
+{
+ dst->untracked = src->untracked;
+ src->untracked = NULL;
+}
diff --git a/ref-filter.c b/ref-filter.c
index 2cc7b01277..ab32bc9c31 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -290,9 +290,9 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)
static void head_atom_parser(struct used_atom *atom, const char *arg)
{
- unsigned char unused[GIT_SHA1_RAWSZ];
+ struct object_id unused;
- atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, unused, NULL);
+ atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, unused.hash, NULL);
}
static struct {
@@ -358,7 +358,7 @@ struct ref_formatting_state {
struct atom_value {
const char *s;
void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
- unsigned long ul; /* used for sorting when not FIELD_STR */
+ uintmax_t value; /* used for sorting when not FIELD_STR */
struct used_atom *atom;
};
@@ -684,13 +684,13 @@ int verify_ref_format(const char *format)
* by the "struct object" representation, set *eaten as well---it is a
* signal from parse_object_buffer to us not to free the buffer.
*/
-static void *get_obj(const unsigned char *sha1, struct object **obj, unsigned long *sz, int *eaten)
+static void *get_obj(const struct object_id *oid, struct object **obj, unsigned long *sz, int *eaten)
{
enum object_type type;
- void *buf = read_sha1_file(sha1, &type, sz);
+ void *buf = read_sha1_file(oid->hash, &type, sz);
if (buf)
- *obj = parse_object_buffer(sha1, type, *sz, buf, eaten);
+ *obj = parse_object_buffer(oid, type, *sz, buf, eaten);
else
*obj = NULL;
return buf;
@@ -730,7 +730,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
if (!strcmp(name, "objecttype"))
v->s = typename(obj->type);
else if (!strcmp(name, "objectsize")) {
- v->ul = sz;
+ v->value = sz;
v->s = xstrfmt("%lu", sz);
}
else if (deref)
@@ -777,8 +777,8 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
}
else if (!strcmp(name, "numparent")) {
- v->ul = commit_list_count(commit->parents);
- v->s = xstrfmt("%lu", v->ul);
+ v->value = commit_list_count(commit->parents);
+ v->s = xstrfmt("%lu", (unsigned long)v->value);
}
else if (!strcmp(name, "parent")) {
struct commit_list *parents;
@@ -856,7 +856,7 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
{
const char *eoemail = strstr(buf, "> ");
char *zone;
- unsigned long timestamp;
+ timestamp_t timestamp;
long tz;
struct date_mode date_mode = { DATE_NORMAL };
const char *formatp;
@@ -875,18 +875,18 @@ static void grab_date(const char *buf, struct atom_value *v, const char *atomnam
if (!eoemail)
goto bad;
- timestamp = strtoul(eoemail + 2, &zone, 10);
- if (timestamp == ULONG_MAX)
+ timestamp = parse_timestamp(eoemail + 2, &zone, 10);
+ if (timestamp == TIME_MAX)
goto bad;
tz = strtol(zone, NULL, 10);
if ((tz == LONG_MIN || tz == LONG_MAX) && errno == ERANGE)
goto bad;
v->s = xstrdup(show_date(timestamp, tz, &date_mode));
- v->ul = timestamp;
+ v->value = timestamp;
return;
bad:
v->s = "";
- v->ul = 0;
+ v->value = 0;
}
/* See grab_values */
@@ -1304,7 +1304,7 @@ static void populate_value(struct ref_array_item *ref)
struct object *obj;
int eaten, i;
unsigned long size;
- const unsigned char *tagged;
+ const struct object_id *tagged;
ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
@@ -1377,7 +1377,7 @@ static void populate_value(struct ref_array_item *ref)
v->s = xstrdup(buf + 1);
}
continue;
- } else if (!deref && grab_objectname(name, ref->objectname, v, atom)) {
+ } else if (!deref && grab_objectname(name, ref->objectname.hash, v, atom)) {
continue;
} else if (!strcmp(name, "HEAD")) {
if (atom->u.head && !strcmp(ref->refname, atom->u.head))
@@ -1421,13 +1421,13 @@ static void populate_value(struct ref_array_item *ref)
return;
need_obj:
- buf = get_obj(ref->objectname, &obj, &size, &eaten);
+ buf = get_obj(&ref->objectname, &obj, &size, &eaten);
if (!buf)
die(_("missing object %s for %s"),
- sha1_to_hex(ref->objectname), ref->refname);
+ oid_to_hex(&ref->objectname), ref->refname);
if (!obj)
die(_("parse_object_buffer failed on %s for %s"),
- sha1_to_hex(ref->objectname), ref->refname);
+ oid_to_hex(&ref->objectname), ref->refname);
grab_values(ref->value, 0, obj, buf, size);
if (!eaten)
@@ -1444,7 +1444,7 @@ static void populate_value(struct ref_array_item *ref)
* If it is a tag object, see if we use a value that derefs
* the object, and if we do grab the object it refers to.
*/
- tagged = ((struct tag *)obj)->tagged->oid.hash;
+ tagged = &((struct tag *)obj)->tagged->oid;
/*
* NEEDSWORK: This derefs tag only once, which
@@ -1455,10 +1455,10 @@ static void populate_value(struct ref_array_item *ref)
buf = get_obj(tagged, &obj, &size, &eaten);
if (!buf)
die(_("missing object %s for %s"),
- sha1_to_hex(tagged), ref->refname);
+ oid_to_hex(tagged), ref->refname);
if (!obj)
die(_("parse_object_buffer failed on %s for %s"),
- sha1_to_hex(tagged), ref->refname);
+ oid_to_hex(tagged), ref->refname);
grab_values(ref->value, 1, obj, buf, size);
if (!eaten)
free(buf);
@@ -1672,6 +1672,68 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
}
/*
+ * Find the longest prefix of pattern we can pass to
+ * `for_each_fullref_in()`, namely the part of pattern preceding the
+ * first glob character. (Note that `for_each_fullref_in()` is
+ * perfectly happy working with a prefix that doesn't end at a
+ * pathname component boundary.)
+ */
+static void find_longest_prefix(struct strbuf *out, const char *pattern)
+{
+ const char *p;
+
+ for (p = pattern; *p && !is_glob_special(*p); p++)
+ ;
+
+ strbuf_add(out, pattern, p - pattern);
+}
+
+/*
+ * This is the same as for_each_fullref_in(), but it tries to iterate
+ * only over the patterns we'll care about. Note that it _doesn't_ do a full
+ * pattern match, so the callback still has to match each ref individually.
+ */
+static int for_each_fullref_in_pattern(struct ref_filter *filter,
+ each_ref_fn cb,
+ void *cb_data,
+ int broken)
+{
+ struct strbuf prefix = STRBUF_INIT;
+ int ret;
+
+ if (!filter->match_as_path) {
+ /*
+ * in this case, the patterns are applied after
+ * prefixes like "refs/heads/" etc. are stripped off,
+ * so we have to look at everything:
+ */
+ return for_each_fullref_in("", cb, cb_data, broken);
+ }
+
+ if (!filter->name_patterns[0]) {
+ /* no patterns; we have to look at everything */
+ return for_each_fullref_in("", cb, cb_data, broken);
+ }
+
+ if (filter->name_patterns[1]) {
+ /*
+ * multiple patterns; in theory this could still work as long
+ * as the patterns are disjoint. We'd just make multiple calls
+ * to for_each_ref(). But if they're not disjoint, we'd end up
+ * reporting the same ref multiple times. So let's punt on that
+ * for now.
+ */
+ return for_each_fullref_in("", cb, cb_data, broken);
+ }
+
+ find_longest_prefix(&prefix, filter->name_patterns[0]);
+
+ ret = for_each_fullref_in(prefix.buf, cb, cb_data, broken);
+ strbuf_release(&prefix);
+ return ret;
+}
+
+/*
* Given a ref (sha1, refname), check if the ref belongs to the array
* of sha1s. If the given ref is a tag, check if the given tag points
* at one of the sha1s in the given sha1 array.
@@ -1693,7 +1755,7 @@ static const struct object_id *match_points_at(struct oid_array *points_at,
if (oid_array_lookup(points_at, oid) >= 0)
return oid;
- obj = parse_object(oid->hash);
+ obj = parse_object(oid);
if (!obj)
die(_("malformed object at '%s'"), refname);
if (obj->type == OBJ_TAG)
@@ -1710,7 +1772,7 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
{
struct ref_array_item *ref;
FLEX_ALLOC_STR(ref, refname, refname);
- hashcpy(ref->objectname, objectname);
+ hashcpy(ref->objectname.hash, objectname);
ref->flag = flag;
return ref;
@@ -1788,7 +1850,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
* non-commits early. The actual filtering is done later.
*/
if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) {
- commit = lookup_commit_reference_gently(oid->hash, 1);
+ commit = lookup_commit_reference_gently(oid, 1);
if (!commit)
return 0;
/* We perform the filtering for the '--contains' option... */
@@ -1917,7 +1979,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
else if (filter->kind == FILTER_REFS_TAGS)
ret = for_each_fullref_in("refs/tags/", ref_filter_handler, &ref_cbdata, broken);
else if (filter->kind & FILTER_REFS_ALL)
- ret = for_each_fullref_in("", ref_filter_handler, &ref_cbdata, broken);
+ ret = for_each_fullref_in_pattern(filter, ref_filter_handler, &ref_cbdata, broken);
if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
head_ref(ref_filter_handler, &ref_cbdata);
}
@@ -1947,9 +2009,9 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
else if (cmp_type == FIELD_STR)
cmp = cmp_fn(va->s, vb->s);
else {
- if (va->ul < vb->ul)
+ if (va->value < vb->value)
cmp = -1;
- else if (va->ul == vb->ul)
+ else if (va->value == vb->value)
cmp = cmp_fn(a->refname, b->refname);
else
cmp = 1;
@@ -2096,7 +2158,7 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
{
struct ref_filter *rf = opt->value;
- unsigned char sha1[20];
+ struct object_id oid;
int no_merged = starts_with(opt->long_name, "no");
if (rf->merge) {
@@ -2111,10 +2173,10 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
? REF_FILTER_MERGED_OMIT
: REF_FILTER_MERGED_INCLUDE;
- if (get_sha1(arg, sha1))
+ if (get_oid(arg, &oid))
die(_("malformed object name %s"), arg);
- rf->merge_commit = lookup_commit_reference_gently(sha1, 0);
+ rf->merge_commit = lookup_commit_reference_gently(&oid, 0);
if (!rf->merge_commit)
return opterror(opt, "must point to a commit", 0);
diff --git a/ref-filter.h b/ref-filter.h
index c20167aa3c..6552024f09 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -34,7 +34,7 @@ struct ref_sorting {
};
struct ref_array_item {
- unsigned char objectname[20];
+ struct object_id objectname;
int flag;
unsigned int kind;
const char *symref;
diff --git a/reflog-walk.c b/reflog-walk.c
index c63eb1a3fd..ed99437ad2 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -12,7 +12,7 @@ struct complete_reflogs {
struct reflog_info {
struct object_id ooid, noid;
char *email;
- unsigned long timestamp;
+ timestamp_t timestamp;
int tz;
char *message;
} *items;
@@ -20,7 +20,7 @@ struct complete_reflogs {
};
static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct complete_reflogs *array = cb_data;
@@ -69,7 +69,7 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
}
static int get_reflog_recno_by_time(struct complete_reflogs *array,
- unsigned long timestamp)
+ timestamp_t timestamp)
{
int i;
for (i = array->nr - 1; i >= 0; i--)
@@ -141,7 +141,7 @@ void init_reflog_walk(struct reflog_walk_info **info)
int add_reflog_for_walk(struct reflog_walk_info *info,
struct commit *commit, const char *name)
{
- unsigned long timestamp = 0;
+ timestamp_t timestamp = 0;
int recno = -1;
struct string_list_item *item;
struct complete_reflogs *reflogs;
@@ -252,13 +252,13 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
do {
reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
commit_reflog->recno--;
- logobj = parse_object(reflog->ooid.hash);
+ logobj = parse_object(&reflog->ooid);
} while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
- if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->ooid.hash)) {
+ if (!logobj && commit_reflog->recno >= 0 && is_null_oid(&reflog->ooid)) {
/* a root commit, but there are still more entries to show */
reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
- logobj = parse_object(reflog->noid.hash);
+ logobj = parse_object(&reflog->noid);
}
if (!logobj || logobj->type != OBJ_COMMIT) {
diff --git a/refs.c b/refs.c
index df75f8e0d6..f0685c9251 100644
--- a/refs.c
+++ b/refs.c
@@ -11,6 +11,7 @@
#include "object.h"
#include "tag.h"
#include "submodule.h"
+#include "worktree.h"
/*
* List of all available backends
@@ -713,7 +714,7 @@ int is_branch(const char *refname)
struct read_ref_at_cb {
const char *refname;
- unsigned long at_time;
+ timestamp_t at_time;
int cnt;
int reccnt;
unsigned char *sha1;
@@ -722,15 +723,15 @@ struct read_ref_at_cb {
unsigned char osha1[20];
unsigned char nsha1[20];
int tz;
- unsigned long date;
+ timestamp_t date;
char **msg;
- unsigned long *cutoff_time;
+ timestamp_t *cutoff_time;
int *cutoff_tz;
int *cutoff_cnt;
};
static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct read_ref_at_cb *cb = cb_data;
@@ -777,7 +778,7 @@ static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
}
static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp,
+ const char *email, timestamp_t timestamp,
int tz, const char *message, void *cb_data)
{
struct read_ref_at_cb *cb = cb_data;
@@ -797,9 +798,9 @@ static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid
return 1;
}
-int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, timestamp_t at_time, int cnt,
unsigned char *sha1, char **msg,
- unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
+ timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
{
struct read_ref_at_cb cb;
@@ -847,11 +848,24 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err)
void ref_transaction_free(struct ref_transaction *transaction)
{
- int i;
+ size_t i;
if (!transaction)
return;
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ case REF_TRANSACTION_CLOSED:
+ /* OK */
+ break;
+ case REF_TRANSACTION_PREPARED:
+ die("BUG: free called on a prepared reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
+
for (i = 0; i < transaction->nr; i++) {
free(transaction->updates[i]->msg);
free(transaction->updates[i]);
@@ -882,9 +896,9 @@ struct ref_update *ref_transaction_add_update(
update->flags = flags;
if (flags & REF_HAVE_NEW)
- hashcpy(update->new_sha1, new_sha1);
+ hashcpy(update->new_oid.hash, new_sha1);
if (flags & REF_HAVE_OLD)
- hashcpy(update->old_sha1, old_sha1);
+ hashcpy(update->old_oid.hash, old_sha1);
update->msg = xstrdup_or_null(msg);
return update;
}
@@ -1245,8 +1259,19 @@ struct ref_iterator *refs_ref_iterator_begin(
{
struct ref_iterator *iter;
+ if (ref_paranoia < 0)
+ ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
+ if (ref_paranoia)
+ flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+
iter = refs->be->iterator_begin(refs, prefix, flags);
- iter = prefix_ref_iterator_begin(iter, prefix, trim);
+
+ /*
+ * `iterator_begin()` already takes care of prefix, but we
+ * might need to do some trimming:
+ */
+ if (trim)
+ iter = prefix_ref_iterator_begin(iter, "", trim);
return iter;
}
@@ -1477,32 +1502,32 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
return 0;
}
-struct submodule_hash_entry
+struct ref_store_hash_entry
{
struct hashmap_entry ent; /* must be the first member! */
struct ref_store *refs;
- /* NUL-terminated name of submodule: */
- char submodule[FLEX_ARRAY];
+ /* NUL-terminated identifier of the ref store: */
+ char name[FLEX_ARRAY];
};
-static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
+static int ref_store_hash_cmp(const void *entry, const void *entry_or_key,
const void *keydata)
{
- const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
- const char *submodule = keydata ? keydata : e2->submodule;
+ const struct ref_store_hash_entry *e1 = entry, *e2 = entry_or_key;
+ const char *name = keydata ? keydata : e2->name;
- return strcmp(e1->submodule, submodule);
+ return strcmp(e1->name, name);
}
-static struct submodule_hash_entry *alloc_submodule_hash_entry(
- const char *submodule, struct ref_store *refs)
+static struct ref_store_hash_entry *alloc_ref_store_hash_entry(
+ const char *name, struct ref_store *refs)
{
- struct submodule_hash_entry *entry;
+ struct ref_store_hash_entry *entry;
- FLEX_ALLOC_STR(entry, submodule, submodule);
- hashmap_entry_init(entry, strhash(submodule));
+ FLEX_ALLOC_STR(entry, name, name);
+ hashmap_entry_init(entry, strhash(name));
entry->refs = refs;
return entry;
}
@@ -1513,20 +1538,23 @@ static struct ref_store *main_ref_store;
/* A hashmap of ref_stores, stored by submodule name: */
static struct hashmap submodule_ref_stores;
+/* A hashmap of ref_stores, stored by worktree id: */
+static struct hashmap worktree_ref_stores;
+
/*
- * Return the ref_store instance for the specified submodule. If that
- * ref_store hasn't been initialized yet, return NULL.
+ * Look up a ref store by name. If that ref_store hasn't been
+ * registered yet, return NULL.
*/
-static struct ref_store *lookup_submodule_ref_store(const char *submodule)
+static struct ref_store *lookup_ref_store_map(struct hashmap *map,
+ const char *name)
{
- struct submodule_hash_entry *entry;
+ struct ref_store_hash_entry *entry;
- if (!submodule_ref_stores.tablesize)
+ if (!map->tablesize)
/* It's initialized on demand in register_ref_store(). */
return NULL;
- entry = hashmap_get_from_hash(&submodule_ref_stores,
- strhash(submodule), submodule);
+ entry = hashmap_get_from_hash(map, strhash(name), name);
return entry ? entry->refs : NULL;
}
@@ -1553,29 +1581,24 @@ struct ref_store *get_main_ref_store(void)
if (main_ref_store)
return main_ref_store;
- main_ref_store = ref_store_init(get_git_dir(),
- (REF_STORE_READ |
- REF_STORE_WRITE |
- REF_STORE_ODB |
- REF_STORE_MAIN));
+ main_ref_store = ref_store_init(get_git_dir(), REF_STORE_ALL_CAPS);
return main_ref_store;
}
/*
- * Register the specified ref_store to be the one that should be used
- * for submodule. It is a fatal error to call this function twice for
- * the same submodule.
+ * Associate a ref store with a name. It is a fatal error to call this
+ * function twice for the same name.
*/
-static void register_submodule_ref_store(struct ref_store *refs,
- const char *submodule)
+static void register_ref_store_map(struct hashmap *map,
+ const char *type,
+ struct ref_store *refs,
+ const char *name)
{
- if (!submodule_ref_stores.tablesize)
- hashmap_init(&submodule_ref_stores, submodule_hash_cmp, 0);
+ if (!map->tablesize)
+ hashmap_init(map, ref_store_hash_cmp, 0);
- if (hashmap_put(&submodule_ref_stores,
- alloc_submodule_hash_entry(submodule, refs)))
- die("BUG: ref_store for submodule '%s' initialized twice",
- submodule);
+ if (hashmap_put(map, alloc_ref_store_hash_entry(name, refs)))
+ die("BUG: %s ref_store '%s' initialized twice", type, name);
}
struct ref_store *get_submodule_ref_store(const char *submodule)
@@ -1592,7 +1615,7 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
return get_main_ref_store();
}
- refs = lookup_submodule_ref_store(submodule);
+ refs = lookup_ref_store_map(&submodule_ref_stores, submodule);
if (refs)
return refs;
@@ -1611,12 +1634,39 @@ struct ref_store *get_submodule_ref_store(const char *submodule)
/* assume that add_submodule_odb() has been called */
refs = ref_store_init(submodule_sb.buf,
REF_STORE_READ | REF_STORE_ODB);
- register_submodule_ref_store(refs, submodule);
+ register_ref_store_map(&submodule_ref_stores, "submodule",
+ refs, submodule);
strbuf_release(&submodule_sb);
return refs;
}
+struct ref_store *get_worktree_ref_store(const struct worktree *wt)
+{
+ struct ref_store *refs;
+ const char *id;
+
+ if (wt->is_current)
+ return get_main_ref_store();
+
+ id = wt->id ? wt->id : "/";
+ refs = lookup_ref_store_map(&worktree_ref_stores, id);
+ if (refs)
+ return refs;
+
+ if (wt->id)
+ refs = ref_store_init(git_common_path("worktrees/%s", wt->id),
+ REF_STORE_ALL_CAPS);
+ else
+ refs = ref_store_init(get_git_common_dir(),
+ REF_STORE_ALL_CAPS);
+
+ if (refs)
+ register_ref_store_map(&worktree_ref_stores, "worktree",
+ refs, id);
+ return refs;
+}
+
void base_ref_store_init(struct ref_store *refs,
const struct ref_storage_be *be)
{
@@ -1657,18 +1707,108 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
refs_heads_master, logmsg);
}
-int ref_transaction_commit(struct ref_transaction *transaction,
- struct strbuf *err)
+int ref_update_reject_duplicates(struct string_list *refnames,
+ struct strbuf *err)
+{
+ size_t i, n = refnames->nr;
+
+ assert(err);
+
+ for (i = 1; i < n; i++) {
+ int cmp = strcmp(refnames->items[i - 1].string,
+ refnames->items[i].string);
+
+ if (!cmp) {
+ strbuf_addf(err,
+ "multiple updates for ref '%s' not allowed.",
+ refnames->items[i].string);
+ return 1;
+ } else if (cmp > 0) {
+ die("BUG: ref_update_reject_duplicates() received unsorted list");
+ }
+ }
+ return 0;
+}
+
+int ref_transaction_prepare(struct ref_transaction *transaction,
+ struct strbuf *err)
{
struct ref_store *refs = transaction->ref_store;
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ /* Good. */
+ break;
+ case REF_TRANSACTION_PREPARED:
+ die("BUG: prepare called twice on reference transaction");
+ break;
+ case REF_TRANSACTION_CLOSED:
+ die("BUG: prepare called on a closed reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
+
if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
strbuf_addstr(err,
_("ref updates forbidden inside quarantine environment"));
return -1;
}
- return refs->be->transaction_commit(refs, transaction, err);
+ return refs->be->transaction_prepare(refs, transaction, err);
+}
+
+int ref_transaction_abort(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = transaction->ref_store;
+ int ret = 0;
+
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ /* No need to abort explicitly. */
+ break;
+ case REF_TRANSACTION_PREPARED:
+ ret = refs->be->transaction_abort(refs, transaction, err);
+ break;
+ case REF_TRANSACTION_CLOSED:
+ die("BUG: abort called on a closed reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
+
+ ref_transaction_free(transaction);
+ return ret;
+}
+
+int ref_transaction_commit(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_store *refs = transaction->ref_store;
+ int ret;
+
+ switch (transaction->state) {
+ case REF_TRANSACTION_OPEN:
+ /* Need to prepare first. */
+ ret = ref_transaction_prepare(transaction, err);
+ if (ret)
+ return ret;
+ break;
+ case REF_TRANSACTION_PREPARED:
+ /* Fall through to finish. */
+ break;
+ case REF_TRANSACTION_CLOSED:
+ die("BUG: commit called on a closed reference transaction");
+ break;
+ default:
+ die("BUG: unexpected reference transaction state");
+ break;
+ }
+
+ return refs->be->transaction_finish(refs, transaction, err);
}
int refs_verify_refname_available(struct ref_store *refs,
@@ -1870,15 +2010,16 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
return refs->be->initial_transaction_commit(refs, transaction, err);
}
-int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
- unsigned int flags)
+int refs_delete_refs(struct ref_store *refs, const char *msg,
+ struct string_list *refnames, unsigned int flags)
{
- return refs->be->delete_refs(refs, refnames, flags);
+ return refs->be->delete_refs(refs, msg, refnames, flags);
}
-int delete_refs(struct string_list *refnames, unsigned int flags)
+int delete_refs(const char *msg, struct string_list *refnames,
+ unsigned int flags)
{
- return refs_delete_refs(get_main_ref_store(), refnames, flags);
+ return refs_delete_refs(get_main_ref_store(), msg, refnames, flags);
}
int refs_rename_ref(struct ref_store *refs, const char *oldref,
diff --git a/refs.h b/refs.h
index 07cf4cd41b..4be14c4b3c 100644
--- a/refs.h
+++ b/refs.h
@@ -5,6 +5,7 @@ struct object_id;
struct ref_store;
struct strbuf;
struct string_list;
+struct worktree;
/*
* Resolve a reference, recursively following symbolic refererences.
@@ -142,30 +143,71 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
/*
- * A ref_transaction represents a collection of ref updates
- * that should succeed or fail together.
+ * A ref_transaction represents a collection of reference updates that
+ * should succeed or fail together.
*
* Calling sequence
* ----------------
+ *
* - Allocate and initialize a `struct ref_transaction` by calling
* `ref_transaction_begin()`.
*
- * - List intended ref updates by calling functions like
- * `ref_transaction_update()` and `ref_transaction_create()`.
- *
- * - Call `ref_transaction_commit()` to execute the transaction.
- * If this succeeds, the ref updates will have taken place and
- * the transaction cannot be rolled back.
- *
- * - Instead of `ref_transaction_commit`, use
- * `initial_ref_transaction_commit()` if the ref database is known
- * to be empty (e.g. during clone). This is likely to be much
- * faster.
- *
- * - At any time call `ref_transaction_free()` to discard the
- * transaction and free associated resources. In particular,
- * this rolls back the transaction if it has not been
- * successfully committed.
+ * - Specify the intended ref updates by calling one or more of the
+ * following functions:
+ * - `ref_transaction_update()`
+ * - `ref_transaction_create()`
+ * - `ref_transaction_delete()`
+ * - `ref_transaction_verify()`
+ *
+ * - Then either:
+ *
+ * - Optionally call `ref_transaction_prepare()` to prepare the
+ * transaction. This locks all references, checks preconditions,
+ * etc. but doesn't finalize anything. If this step fails, the
+ * transaction has been closed and can only be freed. If this step
+ * succeeds, then `ref_transaction_commit()` is almost certain to
+ * succeed. However, you can still call `ref_transaction_abort()`
+ * if you decide not to commit the transaction after all.
+ *
+ * - Call `ref_transaction_commit()` to execute the transaction,
+ * make the changes permanent, and release all locks. If you
+ * haven't already called `ref_transaction_prepare()`, then
+ * `ref_transaction_commit()` calls it for you.
+ *
+ * Or
+ *
+ * - Call `initial_ref_transaction_commit()` if the ref database is
+ * known to be empty and have no other writers (e.g. during
+ * clone). This is likely to be much faster than
+ * `ref_transaction_commit()`. `ref_transaction_prepare()` should
+ * *not* be called before `initial_ref_transaction_commit()`.
+ *
+ * - Then finally, call `ref_transaction_free()` to free the
+ * `ref_transaction` data structure.
+ *
+ * At any time before calling `ref_transaction_commit()`, you can call
+ * `ref_transaction_abort()` to abort the transaction, rollback any
+ * locks, and free any associated resources (including the
+ * `ref_transaction` data structure).
+ *
+ * Putting it all together, a complete reference update looks like
+ *
+ * struct ref_transaction *transaction;
+ * struct strbuf err = STRBUF_INIT;
+ * int ret = 0;
+ *
+ * transaction = ref_store_transaction_begin(refs, &err);
+ * if (!transaction ||
+ * ref_transaction_update(...) ||
+ * ref_transaction_create(...) ||
+ * ...etc... ||
+ * ref_transaction_commit(transaction, &err)) {
+ * error("%s", err.buf);
+ * ret = -1;
+ * }
+ * ref_transaction_free(transaction);
+ * strbuf_release(&err);
+ * return ret;
*
* Error handling
* --------------
@@ -182,8 +224,9 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
* -------
*
* Note that no locks are taken, and no refs are read, until
- * `ref_transaction_commit` is called. So `ref_transaction_verify`
- * won't report a verification failure until the commit is attempted.
+ * `ref_transaction_prepare()` or `ref_transaction_commit()` is
+ * called. So, for example, `ref_transaction_verify()` won't report a
+ * verification failure until the commit is attempted.
*/
struct ref_transaction;
@@ -317,9 +360,9 @@ int safe_create_reflog(const char *refname, int force_create, struct strbuf *err
/** Reads log for the value of ref during at_time. **/
int read_ref_at(const char *refname, unsigned int flags,
- unsigned long at_time, int cnt,
+ timestamp_t at_time, int cnt,
unsigned char *sha1, char **msg,
- unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
+ timestamp_t *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
/** Check if a particular reflog exists */
int refs_reflog_exists(struct ref_store *refs, const char *refname);
@@ -330,7 +373,8 @@ int reflog_exists(const char *refname);
* verify that the current value of the reference is old_sha1 before
* deleting it. If old_sha1 is NULL, delete the reference if it
* exists, regardless of its old value. It is an error for old_sha1 to
- * be NULL_SHA1. flags is passed through to ref_transaction_delete().
+ * be NULL_SHA1. msg and flags are passed through to
+ * ref_transaction_delete().
*/
int refs_delete_ref(struct ref_store *refs, const char *msg,
const char *refname,
@@ -342,12 +386,13 @@ int delete_ref(const char *msg, const char *refname,
/*
* Delete the specified references. If there are any problems, emit
* errors but attempt to keep going (i.e., the deletes are not done in
- * an all-or-nothing transaction). flags is passed through to
+ * an all-or-nothing transaction). msg and flags are passed through to
* ref_transaction_delete().
*/
-int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
- unsigned int flags);
-int delete_refs(struct string_list *refnames, unsigned int flags);
+int refs_delete_refs(struct ref_store *refs, const char *msg,
+ struct string_list *refnames, unsigned int flags);
+int delete_refs(const char *msg, struct string_list *refnames,
+ unsigned int flags);
/** Delete a reflog */
int refs_delete_reflog(struct ref_store *refs, const char *refname);
@@ -356,7 +401,7 @@ int delete_reflog(const char *refname);
/* iterate over reflog entries */
typedef int each_reflog_ent_fn(
struct object_id *old_oid, struct object_id *new_oid,
- const char *committer, unsigned long timestamp,
+ const char *committer, timestamp_t timestamp,
int tz, const char *msg, void *cb_data);
int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
@@ -401,16 +446,6 @@ int refs_create_symref(struct ref_store *refs, const char *refname,
const char *target, const char *logmsg);
int create_symref(const char *refname, const char *target, const char *logmsg);
-/*
- * Update HEAD of the specified gitdir.
- * Similar to create_symref("relative-git-dir/HEAD", target, NULL), but
- * this can update the main working tree's HEAD regardless of where
- * $GIT_DIR points to.
- * Return 0 if successful, non-zero otherwise.
- * */
-int set_worktree_head_symref(const char *gitdir, const char *target,
- const char *logmsg);
-
enum action_on_err {
UPDATE_REFS_MSG_ON_ERR,
UPDATE_REFS_DIE_ON_ERR,
@@ -436,6 +471,19 @@ struct ref_transaction *ref_transaction_begin(struct strbuf *err);
*
* refname -- the name of the reference to be affected.
*
+ * new_sha1 -- the SHA-1 that should be set to be the new value of
+ * the reference. Some functions allow this parameter to be
+ * NULL, meaning that the reference is not changed, or
+ * null_sha1, meaning that the reference should be deleted. A
+ * copy of this value is made in the transaction.
+ *
+ * old_sha1 -- the SHA-1 value that the reference must have before
+ * the update. Some functions allow this parameter to be NULL,
+ * meaning that the old value of the reference is not checked,
+ * or null_sha1, meaning that the reference must not exist
+ * before the update. A copy of this value is made in the
+ * transaction.
+ *
* flags -- flags affecting the update, passed to
* update_ref_lock(). Can be REF_NODEREF, which means that
* symbolic references should not be followed.
@@ -517,20 +565,48 @@ int ref_transaction_verify(struct ref_transaction *transaction,
unsigned int flags,
struct strbuf *err);
-/*
- * Commit all of the changes that have been queued in transaction, as
- * atomically as possible.
- *
- * Returns 0 for success, or one of the below error codes for errors.
- */
/* Naming conflict (for example, the ref names A and A/B conflict). */
#define TRANSACTION_NAME_CONFLICT -1
/* All other errors. */
#define TRANSACTION_GENERIC_ERROR -2
+
+/*
+ * Perform the preparatory stages of commiting `transaction`. Acquire
+ * any needed locks, check preconditions, etc.; basically, do as much
+ * as possible to ensure that the transaction will be able to go
+ * through, stopping just short of making any irrevocable or
+ * user-visible changes. The updates that this function prepares can
+ * be finished up by calling `ref_transaction_commit()` or rolled back
+ * by calling `ref_transaction_abort()`.
+ *
+ * On success, return 0 and leave the transaction in "prepared" state.
+ * On failure, abort the transaction, write an error message to `err`,
+ * and return one of the `TRANSACTION_*` constants.
+ *
+ * Callers who don't need such fine-grained control over commiting
+ * reference transactions should just call `ref_transaction_commit()`.
+ */
+int ref_transaction_prepare(struct ref_transaction *transaction,
+ struct strbuf *err);
+
+/*
+ * Commit all of the changes that have been queued in transaction, as
+ * atomically as possible. On success, return 0 and leave the
+ * transaction in "closed" state. On failure, roll back the
+ * transaction, write an error message to `err`, and return one of the
+ * `TRANSACTION_*` constants
+ */
int ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
/*
+ * Abort `transaction`, which has been begun and possibly prepared,
+ * but not yet committed.
+ */
+int ref_transaction_abort(struct ref_transaction *transaction,
+ struct strbuf *err);
+
+/*
* Like ref_transaction_commit(), but optimized for creating
* references when originally initializing a repository (e.g., by "git
* clone"). It writes the new references directly to packed-refs
@@ -545,7 +621,7 @@ int initial_ref_transaction_commit(struct ref_transaction *transaction,
struct strbuf *err);
/*
- * Free an existing transaction and all associated data.
+ * Free `*transaction` and all associated data.
*/
void ref_transaction_free(struct ref_transaction *transaction);
@@ -611,12 +687,12 @@ enum expire_reflog_flags {
* unlocked again.
*/
typedef void reflog_expiry_prepare_fn(const char *refname,
- const unsigned char *sha1,
+ const struct object_id *oid,
void *cb_data);
-typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
- unsigned char *nsha1,
+typedef int reflog_expiry_should_prune_fn(struct object_id *ooid,
+ struct object_id *noid,
const char *email,
- unsigned long timestamp, int tz,
+ timestamp_t timestamp, int tz,
const char *message, void *cb_data);
typedef void reflog_expiry_cleanup_fn(void *cb_data);
@@ -655,5 +731,6 @@ struct ref_store *get_main_ref_store(void);
* submodule==NULL.
*/
struct ref_store *get_submodule_ref_store(const char *submodule);
+struct ref_store *get_worktree_ref_store(const struct worktree *wt);
#endif /* REFS_H */
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 83ea080e01..d8b3f73147 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -43,15 +43,6 @@ struct packed_ref_cache {
*/
unsigned int referrers;
- /*
- * Iff the packed-refs file associated with this instance is
- * currently locked for writing, this points at the associated
- * lock (which is owned by somebody else). The referrer count
- * is also incremented when the file is locked and decremented
- * when it is unlocked.
- */
- struct lock_file *lock;
-
/* The metadata from when this packed-refs cache was read */
struct stat_validity validity;
};
@@ -70,10 +61,13 @@ struct files_ref_store {
struct ref_cache *loose;
struct packed_ref_cache *packed;
-};
-/* Lock used for the main packed-refs file: */
-static struct lock_file packlock;
+ /*
+ * Lock used for the "packed-refs" file. Note that this (and
+ * thus the enclosing `files_ref_store`) must not be freed.
+ */
+ struct lock_file packed_refs_lock;
+};
/*
* Increment the reference count of *packed_refs.
@@ -104,8 +98,8 @@ static void clear_packed_ref_cache(struct files_ref_store *refs)
if (refs->packed) {
struct packed_ref_cache *packed_refs = refs->packed;
- if (packed_refs->lock)
- die("internal error: packed-ref cache cleared while locked");
+ if (is_lock_file_locked(&refs->packed_refs_lock))
+ die("BUG: packed-ref cache cleared while locked");
refs->packed = NULL;
release_packed_ref_cache(packed_refs);
}
@@ -195,27 +189,15 @@ static const char PACKED_REFS_HEADER[] =
* Return a pointer to the refname within the line (null-terminated),
* or NULL if there was a problem.
*/
-static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
+static const char *parse_ref_line(struct strbuf *line, struct object_id *oid)
{
const char *ref;
- /*
- * 42: the answer to everything.
- *
- * In this case, it happens to be the answer to
- * 40 (length of sha1 hex representation)
- * +1 (space in between hex and name)
- * +1 (newline at the end of the line)
- */
- if (line->len <= 42)
+ if (parse_oid_hex(line->buf, oid, &ref) < 0)
return NULL;
-
- if (get_sha1_hex(line->buf, sha1) < 0)
- return NULL;
- if (!isspace(line->buf[40]))
+ if (!isspace(*ref++))
return NULL;
- ref = line->buf + 41;
if (isspace(*ref))
return NULL;
@@ -227,7 +209,9 @@ static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
}
/*
- * Read f, which is a packed-refs file, into dir.
+ * Read from `packed_refs_file` into a newly-allocated
+ * `packed_ref_cache` and return it. The return value will already
+ * have its reference count incremented.
*
* A comment line of the form "# pack-refs with: " may contain zero or
* more traits. We interpret the traits as follows:
@@ -253,14 +237,38 @@ static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
* compatibility with older clients, but we do not require it
* (i.e., "peeled" is a no-op if "fully-peeled" is set).
*/
-static void read_packed_refs(FILE *f, struct ref_dir *dir)
+static struct packed_ref_cache *read_packed_refs(const char *packed_refs_file)
{
+ FILE *f;
+ struct packed_ref_cache *packed_refs = xcalloc(1, sizeof(*packed_refs));
struct ref_entry *last = NULL;
struct strbuf line = STRBUF_INIT;
enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
+ struct ref_dir *dir;
+
+ acquire_packed_ref_cache(packed_refs);
+ packed_refs->cache = create_ref_cache(NULL, NULL);
+ packed_refs->cache->root->flag &= ~REF_INCOMPLETE;
+
+ f = fopen(packed_refs_file, "r");
+ if (!f) {
+ if (errno == ENOENT) {
+ /*
+ * This is OK; it just means that no
+ * "packed-refs" file has been written yet,
+ * which is equivalent to it being empty.
+ */
+ return packed_refs;
+ } else {
+ die_errno("couldn't read %s", packed_refs_file);
+ }
+ }
+
+ stat_validity_update(&packed_refs->validity, fileno(f));
+ dir = get_ref_dir(packed_refs->cache->root);
while (strbuf_getwholeline(&line, f, '\n') != EOF) {
- unsigned char sha1[20];
+ struct object_id oid;
const char *refname;
const char *traits;
@@ -273,17 +281,17 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
continue;
}
- refname = parse_ref_line(&line, sha1);
+ refname = parse_ref_line(&line, &oid);
if (refname) {
int flag = REF_ISPACKED;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(refname))
die("packed refname is dangerous: %s", refname);
- hashclr(sha1);
+ oidclr(&oid);
flag |= REF_BAD_NAME | REF_ISBROKEN;
}
- last = create_ref_entry(refname, sha1, flag, 0);
+ last = create_ref_entry(refname, &oid, flag);
if (peeled == PEELED_FULLY ||
(peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
last->flag |= REF_KNOWS_PEELED;
@@ -294,8 +302,8 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
line.buf[0] == '^' &&
line.len == PEELED_LINE_LENGTH &&
line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
- !get_sha1_hex(line.buf + 1, sha1)) {
- hashcpy(last->u.value.peeled.hash, sha1);
+ !get_oid_hex(line.buf + 1, &oid)) {
+ oidcpy(&last->u.value.peeled, &oid);
/*
* Regardless of what the file header said,
* we definitely know the value of *this*
@@ -305,7 +313,10 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
}
}
+ fclose(f);
strbuf_release(&line);
+
+ return packed_refs;
}
static const char *files_packed_refs_path(struct files_ref_store *refs)
@@ -360,30 +371,24 @@ static void files_ref_path(struct files_ref_store *refs,
/*
* Get the packed_ref_cache for the specified files_ref_store,
- * creating it if necessary.
+ * creating and populating it if it hasn't been read before or if the
+ * file has been changed (according to its `validity` field) since it
+ * was last read. On the other hand, if we hold the lock, then assume
+ * that the file hasn't been changed out from under us, so skip the
+ * extra `stat()` call in `stat_validity_check()`.
*/
static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
{
const char *packed_refs_file = files_packed_refs_path(refs);
if (refs->packed &&
+ !is_lock_file_locked(&refs->packed_refs_lock) &&
!stat_validity_check(&refs->packed->validity, packed_refs_file))
clear_packed_ref_cache(refs);
- if (!refs->packed) {
- FILE *f;
-
- refs->packed = xcalloc(1, sizeof(*refs->packed));
- acquire_packed_ref_cache(refs->packed);
- refs->packed->cache = create_ref_cache(&refs->base, NULL);
- refs->packed->cache->root->flag &= ~REF_INCOMPLETE;
- f = fopen(packed_refs_file, "r");
- if (f) {
- stat_validity_update(&refs->packed->validity, fileno(f));
- read_packed_refs(f, get_ref_dir(refs->packed->cache->root));
- fclose(f);
- }
- }
+ if (!refs->packed)
+ refs->packed = read_packed_refs(packed_refs_file);
+
return refs->packed;
}
@@ -404,14 +409,18 @@ static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
* commit_packed_refs().
*/
static void add_packed_ref(struct files_ref_store *refs,
- const char *refname, const unsigned char *sha1)
+ const char *refname, const struct object_id *oid)
{
struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
- if (!packed_ref_cache->lock)
- die("internal error: packed refs not locked");
+ if (!is_lock_file_locked(&refs->packed_refs_lock))
+ die("BUG: packed refs not locked");
+
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+ die("Reference has invalid format: '%s'", refname);
+
add_ref_entry(get_packed_ref_dir(packed_ref_cache),
- create_ref_entry(refname, sha1, REF_ISPACKED, 1));
+ create_ref_entry(refname, oid, REF_ISPACKED));
}
/*
@@ -444,7 +453,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
strbuf_add(&refname, dirname, dirnamelen);
while ((de = readdir(d)) != NULL) {
- unsigned char sha1[20];
+ struct object_id oid;
struct stat st;
int flag;
@@ -465,10 +474,10 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
if (!refs_resolve_ref_unsafe(&refs->base,
refname.buf,
RESOLVE_REF_READING,
- sha1, &flag)) {
- hashclr(sha1);
+ oid.hash, &flag)) {
+ oidclr(&oid);
flag |= REF_ISBROKEN;
- } else if (is_null_sha1(sha1)) {
+ } else if (is_null_oid(&oid)) {
/*
* It is so astronomically unlikely
* that NULL_SHA1 is the SHA-1 of an
@@ -484,11 +493,11 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(refname.buf))
die("loose refname is dangerous: %s", refname.buf);
- hashclr(sha1);
+ oidclr(&oid);
flag |= REF_BAD_NAME | REF_ISBROKEN;
}
add_entry_to_dir(dir,
- create_ref_entry(refname.buf, sha1, flag, 0));
+ create_ref_entry(refname.buf, &oid, flag));
}
strbuf_setlen(&refname, dirnamelen);
strbuf_setlen(&path, path_baselen);
@@ -1069,15 +1078,12 @@ static struct ref_iterator *files_ref_iterator_begin(
struct ref_iterator *loose_iter, *packed_iter;
struct files_ref_iterator *iter;
struct ref_iterator *ref_iterator;
+ unsigned int required_flags = REF_STORE_READ;
- if (ref_paranoia < 0)
- ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
- if (ref_paranoia)
- flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+ if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+ required_flags |= REF_STORE_ODB;
- refs = files_downcast(ref_store,
- REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),
- "ref_iterator_begin");
+ refs = files_downcast(ref_store, required_flags, "ref_iterator_begin");
iter = xcalloc(1, sizeof(*iter));
ref_iterator = &iter->base;
@@ -1302,17 +1308,17 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)
}
if (hold_lock_file_for_update_timeout(
- &packlock, files_packed_refs_path(refs),
+ &refs->packed_refs_lock, files_packed_refs_path(refs),
flags, timeout_value) < 0)
return -1;
/*
- * Get the current packed-refs while holding the lock. If the
- * packed-refs file has been modified since we last read it,
- * this will automatically invalidate the cache and re-read
- * the packed-refs file.
+ * Get the current packed-refs while holding the lock. It is
+ * important that we call `get_packed_ref_cache()` before
+ * setting `packed_ref_cache->lock`, because otherwise the
+ * former will see that the file is locked and assume that the
+ * cache can't be stale.
*/
packed_ref_cache = get_packed_ref_cache(refs);
- packed_ref_cache->lock = &packlock;
/* Increment the reference count to prevent it from being freed: */
acquire_packed_ref_cache(packed_ref_cache);
return 0;
@@ -1335,10 +1341,10 @@ static int commit_packed_refs(struct files_ref_store *refs)
files_assert_main_repository(refs, "commit_packed_refs");
- if (!packed_ref_cache->lock)
- die("internal error: packed-refs not locked");
+ if (!is_lock_file_locked(&refs->packed_refs_lock))
+ die("BUG: packed-refs not locked");
- out = fdopen_lock_file(packed_ref_cache->lock, "w");
+ out = fdopen_lock_file(&refs->packed_refs_lock, "w");
if (!out)
die_errno("unable to fdopen packed-refs descriptor");
@@ -1356,11 +1362,10 @@ static int commit_packed_refs(struct files_ref_store *refs)
if (ok != ITER_DONE)
die("error while iterating over references");
- if (commit_lock_file(packed_ref_cache->lock)) {
+ if (commit_lock_file(&refs->packed_refs_lock)) {
save_errno = errno;
error = -1;
}
- packed_ref_cache->lock = NULL;
release_packed_ref_cache(packed_ref_cache);
errno = save_errno;
return error;
@@ -1378,10 +1383,9 @@ static void rollback_packed_refs(struct files_ref_store *refs)
files_assert_main_repository(refs, "rollback_packed_refs");
- if (!packed_ref_cache->lock)
- die("internal error: packed-refs not locked");
- rollback_lock_file(packed_ref_cache->lock);
- packed_ref_cache->lock = NULL;
+ if (!is_lock_file_locked(&refs->packed_refs_lock))
+ die("BUG: packed-refs not locked");
+ rollback_lock_file(&refs->packed_refs_lock);
release_packed_ref_cache(packed_ref_cache);
clear_packed_ref_cache(refs);
}
@@ -1476,6 +1480,32 @@ static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)
}
}
+/*
+ * Return true if the specified reference should be packed.
+ */
+static int should_pack_ref(const char *refname,
+ const struct object_id *oid, unsigned int ref_flags,
+ unsigned int pack_flags)
+{
+ /* Do not pack per-worktree refs: */
+ if (ref_type(refname) != REF_TYPE_NORMAL)
+ return 0;
+
+ /* Do not pack non-tags unless PACK_REFS_ALL is set: */
+ if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/"))
+ return 0;
+
+ /* Do not pack symbolic refs: */
+ if (ref_flags & REF_ISSYMREF)
+ return 0;
+
+ /* Do not pack broken refs: */
+ if (!ref_resolves_to_object(refname, oid, ref_flags))
+ return 0;
+
+ return 1;
+}
+
static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
{
struct files_ref_store *refs =
@@ -1497,21 +1527,9 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
* pruned, also add it to refs_to_prune.
*/
struct ref_entry *packed_entry;
- int is_tag_ref = starts_with(iter->refname, "refs/tags/");
-
- /* Do not pack per-worktree refs: */
- if (ref_type(iter->refname) != REF_TYPE_NORMAL)
- continue;
- /* ALWAYS pack tags */
- if (!(flags & PACK_REFS_ALL) && !is_tag_ref)
- continue;
-
- /* Do not pack symbolic or broken refs: */
- if (iter->flags & REF_ISSYMREF)
- continue;
-
- if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags))
+ if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
+ flags))
continue;
/*
@@ -1526,8 +1544,8 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
packed_entry->flag = REF_ISPACKED;
oidcpy(&packed_entry->u.value.oid, iter->oid);
} else {
- packed_entry = create_ref_entry(iter->refname, iter->oid->hash,
- REF_ISPACKED, 0);
+ packed_entry = create_ref_entry(iter->refname, iter->oid,
+ REF_ISPACKED);
add_ref_entry(packed_refs, packed_entry);
}
oidclr(&packed_entry->u.value.peeled);
@@ -1607,7 +1625,7 @@ static int repack_without_refs(struct files_ref_store *refs,
return ret;
}
-static int files_delete_refs(struct ref_store *ref_store,
+static int files_delete_refs(struct ref_store *ref_store, const char *msg,
struct string_list *refnames, unsigned int flags)
{
struct files_ref_store *refs =
@@ -1639,7 +1657,7 @@ static int files_delete_refs(struct ref_store *ref_store,
for (i = 0; i < refnames->nr; i++) {
const char *refname = refnames->items[i].string;
- if (refs_delete_ref(&refs->base, NULL, refname, NULL, flags))
+ if (refs_delete_ref(&refs->base, msg, refname, NULL, flags))
result |= error(_("could not remove reference %s"), refname);
}
@@ -1709,10 +1727,10 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
}
static int write_ref_to_lockfile(struct ref_lock *lock,
- const unsigned char *sha1, struct strbuf *err);
+ const struct object_id *oid, struct strbuf *err);
static int commit_ref_update(struct files_ref_store *refs,
struct ref_lock *lock,
- const unsigned char *sha1, const char *logmsg,
+ const struct object_id *oid, const char *logmsg,
struct strbuf *err);
static int files_rename_ref(struct ref_store *ref_store,
@@ -1721,7 +1739,7 @@ static int files_rename_ref(struct ref_store *ref_store,
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
- unsigned char sha1[20], orig_sha1[20];
+ struct object_id oid, orig_oid;
int flag = 0, logmoved = 0;
struct ref_lock *lock;
struct stat loginfo;
@@ -1743,7 +1761,7 @@ static int files_rename_ref(struct ref_store *ref_store,
if (!refs_resolve_ref_unsafe(&refs->base, oldrefname,
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- orig_sha1, &flag)) {
+ orig_oid.hash, &flag)) {
ret = error("refname %s not found", oldrefname);
goto out;
}
@@ -1765,21 +1783,21 @@ static int files_rename_ref(struct ref_store *ref_store,
}
if (refs_delete_ref(&refs->base, logmsg, oldrefname,
- orig_sha1, REF_NODEREF)) {
+ orig_oid.hash, REF_NODEREF)) {
error("unable to delete old %s", oldrefname);
goto rollback;
}
/*
- * Since we are doing a shallow lookup, sha1 is not the
- * correct value to pass to delete_ref as old_sha1. But that
- * doesn't matter, because an old_sha1 check wouldn't add to
+ * Since we are doing a shallow lookup, oid is not the
+ * correct value to pass to delete_ref as old_oid. But that
+ * doesn't matter, because an old_oid check wouldn't add to
* the safety anyway; we want to delete the reference whatever
* its current value.
*/
if (!refs_read_ref_full(&refs->base, newrefname,
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- sha1, NULL) &&
+ oid.hash, NULL) &&
refs_delete_ref(&refs->base, NULL, newrefname,
NULL, REF_NODEREF)) {
if (errno == EISDIR) {
@@ -1812,10 +1830,10 @@ static int files_rename_ref(struct ref_store *ref_store,
strbuf_release(&err);
goto rollback;
}
- hashcpy(lock->old_oid.hash, orig_sha1);
+ oidcpy(&lock->old_oid, &orig_oid);
- if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
- commit_ref_update(refs, lock, orig_sha1, logmsg, &err)) {
+ if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+ commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) {
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
strbuf_release(&err);
goto rollback;
@@ -1835,8 +1853,8 @@ static int files_rename_ref(struct ref_store *ref_store,
flag = log_all_ref_updates;
log_all_ref_updates = LOG_REFS_NONE;
- if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
- commit_ref_update(refs, lock, orig_sha1, NULL, &err)) {
+ if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+ commit_ref_update(refs, lock, &orig_oid, NULL, &err)) {
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
strbuf_release(&err);
}
@@ -1986,8 +2004,8 @@ static int files_create_reflog(struct ref_store *ref_store,
return 0;
}
-static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
- const unsigned char *new_sha1,
+static int log_ref_write_fd(int fd, const struct object_id *old_oid,
+ const struct object_id *new_oid,
const char *committer, const char *msg)
{
int msglen, written;
@@ -1998,8 +2016,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
maxlen = strlen(committer) + msglen + 100;
logrec = xmalloc(maxlen);
len = xsnprintf(logrec, maxlen, "%s %s %s\n",
- sha1_to_hex(old_sha1),
- sha1_to_hex(new_sha1),
+ oid_to_hex(old_oid),
+ oid_to_hex(new_oid),
committer);
if (msglen)
len += copy_reflog_msg(logrec + len - 1, msg) - 1;
@@ -2013,8 +2031,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
}
static int files_log_ref_write(struct files_ref_store *refs,
- const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg,
+ const char *refname, const struct object_id *old_oid,
+ const struct object_id *new_oid, const char *msg,
int flags, struct strbuf *err)
{
int logfd, result;
@@ -2031,7 +2049,7 @@ static int files_log_ref_write(struct files_ref_store *refs,
if (logfd < 0)
return 0;
- result = log_ref_write_fd(logfd, old_sha1, new_sha1,
+ result = log_ref_write_fd(logfd, old_oid, new_oid,
git_committer_info(0), msg);
if (result) {
struct strbuf sb = STRBUF_INIT;
@@ -2063,29 +2081,29 @@ static int files_log_ref_write(struct files_ref_store *refs,
* return -1.
*/
static int write_ref_to_lockfile(struct ref_lock *lock,
- const unsigned char *sha1, struct strbuf *err)
+ const struct object_id *oid, struct strbuf *err)
{
static char term = '\n';
struct object *o;
int fd;
- o = parse_object(sha1);
+ o = parse_object(oid);
if (!o) {
strbuf_addf(err,
"trying to write ref '%s' with nonexistent object %s",
- lock->ref_name, sha1_to_hex(sha1));
+ lock->ref_name, oid_to_hex(oid));
unlock_ref(lock);
return -1;
}
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
strbuf_addf(err,
"trying to write non-commit object %s to branch '%s'",
- sha1_to_hex(sha1), lock->ref_name);
+ oid_to_hex(oid), lock->ref_name);
unlock_ref(lock);
return -1;
}
fd = get_lock_file_fd(lock->lk);
- if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 ||
+ if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
write_in_full(fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
strbuf_addf(err,
@@ -2103,14 +2121,14 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
*/
static int commit_ref_update(struct files_ref_store *refs,
struct ref_lock *lock,
- const unsigned char *sha1, const char *logmsg,
+ const struct object_id *oid, const char *logmsg,
struct strbuf *err)
{
files_assert_main_repository(refs, "commit_ref_update");
clear_loose_ref_cache(refs);
if (files_log_ref_write(refs, lock->ref_name,
- lock->old_oid.hash, sha1,
+ &lock->old_oid, oid,
logmsg, 0, err)) {
char *old_msg = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -2133,18 +2151,18 @@ static int commit_ref_update(struct files_ref_store *refs,
* check with HEAD only which should cover 99% of all usage
* scenarios (even 100% of the default ones).
*/
- unsigned char head_sha1[20];
+ struct object_id head_oid;
int head_flag;
const char *head_ref;
head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD",
RESOLVE_REF_READING,
- head_sha1, &head_flag);
+ head_oid.hash, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name)) {
struct strbuf log_err = STRBUF_INIT;
if (files_log_ref_write(refs, "HEAD",
- lock->old_oid.hash, sha1,
+ &lock->old_oid, oid,
logmsg, 0, &log_err)) {
error("%s", log_err.buf);
strbuf_release(&log_err);
@@ -2182,12 +2200,12 @@ static void update_symref_reflog(struct files_ref_store *refs,
const char *target, const char *logmsg)
{
struct strbuf err = STRBUF_INIT;
- unsigned char new_sha1[20];
+ struct object_id new_oid;
if (logmsg &&
!refs_read_ref_full(&refs->base, target,
- RESOLVE_REF_READING, new_sha1, NULL) &&
- files_log_ref_write(refs, refname, lock->old_oid.hash,
- new_sha1, logmsg, 0, &err)) {
+ RESOLVE_REF_READING, new_oid.hash, NULL) &&
+ files_log_ref_write(refs, refname, &lock->old_oid,
+ &new_oid, logmsg, 0, &err)) {
error("%s", err.buf);
strbuf_release(&err);
}
@@ -2240,50 +2258,6 @@ static int files_create_symref(struct ref_store *ref_store,
return ret;
}
-int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)
-{
- /*
- * FIXME: this obviously will not work well for future refs
- * backends. This function needs to die.
- */
- struct files_ref_store *refs =
- files_downcast(get_main_ref_store(),
- REF_STORE_WRITE,
- "set_head_symref");
-
- static struct lock_file head_lock;
- struct ref_lock *lock;
- struct strbuf head_path = STRBUF_INIT;
- const char *head_rel;
- int ret;
-
- strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir));
- if (hold_lock_file_for_update(&head_lock, head_path.buf,
- LOCK_NO_DEREF) < 0) {
- struct strbuf err = STRBUF_INIT;
- unable_to_lock_message(head_path.buf, errno, &err);
- error("%s", err.buf);
- strbuf_release(&err);
- strbuf_release(&head_path);
- return -1;
- }
-
- /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for
- linked trees */
- head_rel = remove_leading_path(head_path.buf,
- absolute_path(get_git_common_dir()));
- /* to make use of create_symref_locked(), initialize ref_lock */
- lock = xcalloc(1, sizeof(struct ref_lock));
- lock->lk = &head_lock;
- lock->ref_name = xstrdup(head_rel);
-
- ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
-
- unlock_ref(lock); /* will free lock */
- strbuf_release(&head_path);
- return ret;
-}
-
static int files_reflog_exists(struct ref_store *ref_store,
const char *refname)
{
@@ -2317,7 +2291,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
{
struct object_id ooid, noid;
char *email_end, *message;
- unsigned long timestamp;
+ timestamp_t timestamp;
int tz;
const char *p = sb->buf;
@@ -2327,7 +2301,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
!(email_end = strchr(p, '>')) ||
email_end[1] != ' ' ||
- !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+ !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
!message || message[0] != ' ' ||
(message[1] != '+' && message[1] != '-') ||
!isdigit(message[2]) || !isdigit(message[3]) ||
@@ -2577,23 +2551,6 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
return ref_iterator;
}
-static int ref_update_reject_duplicates(struct string_list *refnames,
- struct strbuf *err)
-{
- int i, n = refnames->nr;
-
- assert(err);
-
- for (i = 1; i < n; i++)
- if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) {
- strbuf_addf(err,
- "multiple updates for ref '%s' not allowed.",
- refnames->items[i].string);
- return 1;
- }
- return 0;
-}
-
/*
* If update is a direct update of head_ref (the reference pointed to
* by HEAD), then add an extra REF_LOG_ONLY update for HEAD.
@@ -2633,7 +2590,7 @@ static int split_head_update(struct ref_update *update,
new_update = ref_transaction_add_update(
transaction, "HEAD",
update->flags | REF_LOG_ONLY | REF_NODEREF,
- update->new_sha1, update->old_sha1,
+ update->new_oid.hash, update->old_oid.hash,
update->msg);
item->util = new_update;
@@ -2690,7 +2647,7 @@ static int split_symref_update(struct files_ref_store *refs,
new_update = ref_transaction_add_update(
transaction, referent, new_flags,
- update->new_sha1, update->old_sha1,
+ update->new_oid.hash, update->old_oid.hash,
update->msg);
new_update->parent_update = update;
@@ -2729,10 +2686,10 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
struct strbuf *err)
{
if (!(update->flags & REF_HAVE_OLD) ||
- !hashcmp(oid->hash, update->old_sha1))
+ !oidcmp(oid, &update->old_oid))
return 0;
- if (is_null_sha1(update->old_sha1))
+ if (is_null_oid(&update->old_oid))
strbuf_addf(err, "cannot lock ref '%s': "
"reference already exists",
original_update_refname(update));
@@ -2740,13 +2697,13 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
strbuf_addf(err, "cannot lock ref '%s': "
"reference is missing but expected %s",
original_update_refname(update),
- sha1_to_hex(update->old_sha1));
+ oid_to_hex(&update->old_oid));
else
strbuf_addf(err, "cannot lock ref '%s': "
"is at %s but expected %s",
original_update_refname(update),
oid_to_hex(oid),
- sha1_to_hex(update->old_sha1));
+ oid_to_hex(&update->old_oid));
return -1;
}
@@ -2773,13 +2730,13 @@ static int lock_ref_for_update(struct files_ref_store *refs,
{
struct strbuf referent = STRBUF_INIT;
int mustexist = (update->flags & REF_HAVE_OLD) &&
- !is_null_sha1(update->old_sha1);
+ !is_null_oid(&update->old_oid);
int ret;
struct ref_lock *lock;
files_assert_main_repository(refs, "lock_ref_for_update");
- if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
+ if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
update->flags |= REF_DELETING;
if (head_ref) {
@@ -2861,12 +2818,12 @@ static int lock_ref_for_update(struct files_ref_store *refs,
!(update->flags & REF_DELETING) &&
!(update->flags & REF_LOG_ONLY)) {
if (!(update->type & REF_ISSYMREF) &&
- !hashcmp(lock->old_oid.hash, update->new_sha1)) {
+ !oidcmp(&lock->old_oid, &update->new_oid)) {
/*
* The reference already has the desired
* value, so we don't need to write it.
*/
- } else if (write_ref_to_lockfile(lock, update->new_sha1,
+ } else if (write_ref_to_lockfile(lock, &update->new_oid,
err)) {
char *write_err = strbuf_detach(err, NULL);
@@ -2899,31 +2856,45 @@ static int lock_ref_for_update(struct files_ref_store *refs,
return 0;
}
-static int files_transaction_commit(struct ref_store *ref_store,
- struct ref_transaction *transaction,
- struct strbuf *err)
+/*
+ * Unlock any references in `transaction` that are still locked, and
+ * mark the transaction closed.
+ */
+static void files_transaction_cleanup(struct ref_transaction *transaction)
+{
+ size_t i;
+
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *update = transaction->updates[i];
+ struct ref_lock *lock = update->backend_data;
+
+ if (lock) {
+ unlock_ref(lock);
+ update->backend_data = NULL;
+ }
+ }
+
+ transaction->state = REF_TRANSACTION_CLOSED;
+}
+
+static int files_transaction_prepare(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE,
- "ref_transaction_commit");
- int ret = 0, i;
- struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
- struct string_list_item *ref_to_delete;
+ "ref_transaction_prepare");
+ size_t i;
+ int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
char *head_ref = NULL;
int head_type;
struct object_id head_oid;
- struct strbuf sb = STRBUF_INIT;
assert(err);
- if (transaction->state != REF_TRANSACTION_OPEN)
- die("BUG: commit called for transaction that is not open");
-
- if (!transaction->nr) {
- transaction->state = REF_TRANSACTION_CLOSED;
- return 0;
- }
+ if (!transaction->nr)
+ goto cleanup;
/*
* Fail if a refname appears more than once in the
@@ -2982,6 +2953,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
* that new values are valid, and write new values to the
* lockfiles, ready to be activated. Only keep one lockfile
* open at a time to avoid running out of file descriptors.
+ * Note that lock_ref_for_update() might append more updates
+ * to the transaction.
*/
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
@@ -2989,7 +2962,38 @@ static int files_transaction_commit(struct ref_store *ref_store,
ret = lock_ref_for_update(refs, update, transaction,
head_ref, &affected_refnames, err);
if (ret)
- goto cleanup;
+ break;
+ }
+
+cleanup:
+ free(head_ref);
+ string_list_clear(&affected_refnames, 0);
+
+ if (ret)
+ files_transaction_cleanup(transaction);
+ else
+ transaction->state = REF_TRANSACTION_PREPARED;
+
+ return ret;
+}
+
+static int files_transaction_finish(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, 0, "ref_transaction_finish");
+ size_t i;
+ int ret = 0;
+ struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
+ struct string_list_item *ref_to_delete;
+ struct strbuf sb = STRBUF_INIT;
+
+ assert(err);
+
+ if (!transaction->nr) {
+ transaction->state = REF_TRANSACTION_CLOSED;
+ return 0;
}
/* Perform updates first so live commits remain referenced */
@@ -3001,8 +3005,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
update->flags & REF_LOG_ONLY) {
if (files_log_ref_write(refs,
lock->ref_name,
- lock->old_oid.hash,
- update->new_sha1,
+ &lock->old_oid,
+ &update->new_oid,
update->msg, update->flags,
err)) {
char *old_msg = strbuf_detach(err, NULL);
@@ -3069,15 +3073,10 @@ static int files_transaction_commit(struct ref_store *ref_store,
clear_loose_ref_cache(refs);
cleanup:
- strbuf_release(&sb);
- transaction->state = REF_TRANSACTION_CLOSED;
+ files_transaction_cleanup(transaction);
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
- struct ref_lock *lock = update->backend_data;
-
- if (lock)
- unlock_ref(lock);
if (update->flags & REF_DELETED_LOOSE) {
/*
@@ -3091,13 +3090,19 @@ cleanup:
}
}
+ strbuf_release(&sb);
string_list_clear(&refs_to_delete, 0);
- free(head_ref);
- string_list_clear(&affected_refnames, 0);
-
return ret;
}
+static int files_transaction_abort(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ files_transaction_cleanup(transaction);
+ return 0;
+}
+
static int ref_present(const char *refname,
const struct object_id *oid, int flags, void *cb_data)
{
@@ -3113,7 +3118,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE,
"initial_ref_transaction_commit");
- int ret = 0, i;
+ size_t i;
+ int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
assert(err);
@@ -3151,7 +3157,7 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
struct ref_update *update = transaction->updates[i];
if ((update->flags & REF_HAVE_OLD) &&
- !is_null_sha1(update->old_sha1))
+ !is_null_oid(&update->old_oid))
die("BUG: initial ref transaction with old_sha1 set");
if (refs_verify_refname_available(&refs->base, update->refname,
&affected_refnames, NULL,
@@ -3172,8 +3178,9 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
struct ref_update *update = transaction->updates[i];
if ((update->flags & REF_HAVE_NEW) &&
- !is_null_sha1(update->new_sha1))
- add_packed_ref(refs, update->refname, update->new_sha1);
+ !is_null_oid(&update->new_oid))
+ add_packed_ref(refs, update->refname,
+ &update->new_oid);
}
if (commit_packed_refs(refs)) {
@@ -3198,7 +3205,7 @@ struct expire_reflog_cb {
};
static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct expire_reflog_cb *cb = cb_data;
@@ -3207,7 +3214,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
if (cb->flags & EXPIRE_REFLOGS_REWRITE)
ooid = &cb->last_kept_oid;
- if ((*cb->should_prune_fn)(ooid->hash, noid->hash, email, timestamp, tz,
+ if ((*cb->should_prune_fn)(ooid, noid, email, timestamp, tz,
message, policy_cb)) {
if (!cb->newlog)
printf("would prune %s", message);
@@ -3215,7 +3222,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
printf("prune %s", message);
} else {
if (cb->newlog) {
- fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+ fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
oid_to_hex(ooid), oid_to_hex(noid),
email, timestamp, tz, message);
oidcpy(&cb->last_kept_oid, noid);
@@ -3244,6 +3251,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
int status = 0;
int type;
struct strbuf err = STRBUF_INIT;
+ struct object_id oid;
memset(&cb, 0, sizeof(cb));
cb.flags = flags;
@@ -3293,7 +3301,9 @@ static int files_reflog_expire(struct ref_store *ref_store,
}
}
- (*prepare_fn)(refname, sha1, cb.policy_cb);
+ hashcpy(oid.hash, sha1);
+
+ (*prepare_fn)(refname, &oid, cb.policy_cb);
refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);
(*cleanup_fn)(cb.policy_cb);
@@ -3363,7 +3373,9 @@ struct ref_storage_be refs_be_files = {
"files",
files_ref_store_create,
files_init_db,
- files_transaction_commit,
+ files_transaction_prepare,
+ files_transaction_finish,
+ files_transaction_abort,
files_initial_transaction_commit,
files_pack_refs,
diff --git a/refs/iterator.c b/refs/iterator.c
index bce1f192f7..4cf449ef66 100644
--- a/refs/iterator.c
+++ b/refs/iterator.c
@@ -292,7 +292,23 @@ static int prefix_ref_iterator_advance(struct ref_iterator *ref_iterator)
if (!starts_with(iter->iter0->refname, iter->prefix))
continue;
- iter->base.refname = iter->iter0->refname + iter->trim;
+ if (iter->trim) {
+ /*
+ * It is nonsense to trim off characters that
+ * you haven't already checked for via a
+ * prefix check, whether via this
+ * `prefix_ref_iterator` or upstream in
+ * `iter0`). So if there wouldn't be at least
+ * one character left in the refname after
+ * trimming, report it as a bug:
+ */
+ if (strlen(iter->iter0->refname) <= iter->trim)
+ die("BUG: attempt to trim too many characters");
+ iter->base.refname = iter->iter0->refname + iter->trim;
+ } else {
+ iter->base.refname = iter->iter0->refname;
+ }
+
iter->base.oid = iter->iter0->oid;
iter->base.flags = iter->iter0->flags;
return ITER_OK;
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
index 6059362f1d..af2fcb2c12 100644
--- a/refs/ref-cache.c
+++ b/refs/ref-cache.c
@@ -32,16 +32,12 @@ struct ref_dir *get_ref_dir(struct ref_entry *entry)
}
struct ref_entry *create_ref_entry(const char *refname,
- const unsigned char *sha1, int flag,
- int check_name)
+ const struct object_id *oid, int flag)
{
struct ref_entry *ref;
- if (check_name &&
- check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
- die("Reference has invalid format: '%s'", refname);
FLEX_ALLOC_STR(ref, name, refname);
- hashcpy(ref->u.value.oid.hash, sha1);
+ oidcpy(&ref->u.value.oid, oid);
oidclr(&ref->u.value.peeled);
ref->flag = flag;
return ref;
@@ -316,11 +312,42 @@ static void sort_ref_dir(struct ref_dir *dir)
dir->sorted = dir->nr = i;
}
+enum prefix_state {
+ /* All refs within the directory would match prefix: */
+ PREFIX_CONTAINS_DIR,
+
+ /* Some, but not all, refs within the directory might match prefix: */
+ PREFIX_WITHIN_DIR,
+
+ /* No refs within the directory could possibly match prefix: */
+ PREFIX_EXCLUDES_DIR
+};
+
/*
- * Load all of the refs from `dir` (recursively) into our in-memory
- * cache.
+ * Return a `prefix_state` constant describing the relationship
+ * between the directory with the specified `dirname` and `prefix`.
*/
-static void prime_ref_dir(struct ref_dir *dir)
+static enum prefix_state overlaps_prefix(const char *dirname,
+ const char *prefix)
+{
+ while (*prefix && *dirname == *prefix) {
+ dirname++;
+ prefix++;
+ }
+ if (!*prefix)
+ return PREFIX_CONTAINS_DIR;
+ else if (!*dirname)
+ return PREFIX_WITHIN_DIR;
+ else
+ return PREFIX_EXCLUDES_DIR;
+}
+
+/*
+ * Load all of the refs from `dir` (recursively) that could possibly
+ * contain references matching `prefix` into our in-memory cache. If
+ * `prefix` is NULL, prime unconditionally.
+ */
+static void prime_ref_dir(struct ref_dir *dir, const char *prefix)
{
/*
* The hard work of loading loose refs is done by get_ref_dir(), so we
@@ -331,8 +358,29 @@ static void prime_ref_dir(struct ref_dir *dir)
int i;
for (i = 0; i < dir->nr; i++) {
struct ref_entry *entry = dir->entries[i];
- if (entry->flag & REF_DIR)
- prime_ref_dir(get_ref_dir(entry));
+ if (!(entry->flag & REF_DIR)) {
+ /* Not a directory; no need to recurse. */
+ } else if (!prefix) {
+ /* Recurse in any case: */
+ prime_ref_dir(get_ref_dir(entry), NULL);
+ } else {
+ switch (overlaps_prefix(entry->name, prefix)) {
+ case PREFIX_CONTAINS_DIR:
+ /*
+ * Recurse, and from here down we
+ * don't have to check the prefix
+ * anymore:
+ */
+ prime_ref_dir(get_ref_dir(entry), NULL);
+ break;
+ case PREFIX_WITHIN_DIR:
+ prime_ref_dir(get_ref_dir(entry), prefix);
+ break;
+ case PREFIX_EXCLUDES_DIR:
+ /* No need to prime this directory. */
+ break;
+ }
+ }
}
}
@@ -347,6 +395,8 @@ struct cache_ref_iterator_level {
*/
struct ref_dir *dir;
+ enum prefix_state prefix_state;
+
/*
* The index of the current entry within dir (which might
* itself be a directory). If index == -1, then the iteration
@@ -374,6 +424,13 @@ struct cache_ref_iterator {
size_t levels_alloc;
/*
+ * Only include references with this prefix in the iteration.
+ * The prefix is matched textually, without regard for path
+ * component boundaries.
+ */
+ const char *prefix;
+
+ /*
* A stack of levels. levels[0] is the uppermost level that is
* being iterated over in this iteration. (This is not
* necessary the top level in the references hierarchy. If we
@@ -394,6 +451,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
&iter->levels[iter->levels_nr - 1];
struct ref_dir *dir = level->dir;
struct ref_entry *entry;
+ enum prefix_state entry_prefix_state;
if (level->index == -1)
sort_ref_dir(dir);
@@ -408,6 +466,14 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
entry = dir->entries[level->index];
+ if (level->prefix_state == PREFIX_WITHIN_DIR) {
+ entry_prefix_state = overlaps_prefix(entry->name, iter->prefix);
+ if (entry_prefix_state == PREFIX_EXCLUDES_DIR)
+ continue;
+ } else {
+ entry_prefix_state = level->prefix_state;
+ }
+
if (entry->flag & REF_DIR) {
/* push down a level */
ALLOC_GROW(iter->levels, iter->levels_nr + 1,
@@ -415,6 +481,7 @@ static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
level = &iter->levels[iter->levels_nr++];
level->dir = get_ref_dir(entry);
+ level->prefix_state = entry_prefix_state;
level->index = -1;
} else {
iter->base.refname = entry->name;
@@ -475,6 +542,7 @@ static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
struct cache_ref_iterator *iter =
(struct cache_ref_iterator *)ref_iterator;
+ free((char *)iter->prefix);
free(iter->levels);
base_ref_iterator_free(ref_iterator);
return ITER_DONE;
@@ -500,10 +568,10 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
dir = find_containing_dir(dir, prefix, 0);
if (!dir)
/* There's nothing to iterate over. */
- return empty_ref_iterator_begin();
+ return empty_ref_iterator_begin();
if (prime_dir)
- prime_ref_dir(dir);
+ prime_ref_dir(dir, prefix);
iter = xcalloc(1, sizeof(*iter));
ref_iterator = &iter->base;
@@ -515,9 +583,12 @@ struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
level->index = -1;
level->dir = dir;
- if (prefix && *prefix)
- ref_iterator = prefix_ref_iterator_begin(ref_iterator,
- prefix, 0);
+ if (prefix && *prefix) {
+ iter->prefix = xstrdup(prefix);
+ level->prefix_state = PREFIX_WITHIN_DIR;
+ } else {
+ level->prefix_state = PREFIX_CONTAINS_DIR;
+ }
return ref_iterator;
}
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
index ffdc54f3f0..794f000fd3 100644
--- a/refs/ref-cache.h
+++ b/refs/ref-cache.h
@@ -185,8 +185,7 @@ struct ref_entry *create_dir_entry(struct ref_cache *cache,
int incomplete);
struct ref_entry *create_ref_entry(const char *refname,
- const unsigned char *sha1, int flag,
- int check_name);
+ const struct object_id *oid, int flag);
/*
* Return a pointer to a new `ref_cache`. Its top-level starts out
@@ -194,7 +193,8 @@ struct ref_entry *create_ref_entry(const char *refname,
* function called to fill in incomplete directories in the
* `ref_cache` when they are accessed. If it is NULL, then the whole
* `ref_cache` must be filled (including clearing its directories'
- * `REF_INCOMPLETE` bits) before it is used.
+ * `REF_INCOMPLETE` bits) before it is used, and `refs` can be NULL,
+ * too.
*/
struct ref_cache *create_ref_cache(struct ref_store *refs,
fill_ref_dir_fn *fill_ref_dir);
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
index 3d46131efb..192f9f85c9 100644
--- a/refs/refs-internal.h
+++ b/refs/refs-internal.h
@@ -130,13 +130,13 @@ struct ref_update {
/*
* If (flags & REF_HAVE_NEW), set the reference to this value:
*/
- unsigned char new_sha1[20];
+ struct object_id new_oid;
/*
* If (flags & REF_HAVE_OLD), check that the reference
* previously had this value:
*/
- unsigned char old_sha1[20];
+ struct object_id old_oid;
/*
* One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
@@ -170,6 +170,14 @@ int refs_read_raw_ref(struct ref_store *ref_store,
struct strbuf *referent, unsigned int *type);
/*
+ * Write an error to `err` and return a nonzero value iff the same
+ * refname appears multiple times in `refnames`. `refnames` must be
+ * sorted on entry to this function.
+ */
+int ref_update_reject_duplicates(struct string_list *refnames,
+ struct strbuf *err);
+
+/*
* Add a ref_update with the specified properties to transaction, and
* return a pointer to the new object. This function does not verify
* that refname is well-formed. new_sha1 and old_sha1 are only
@@ -185,17 +193,27 @@ struct ref_update *ref_transaction_add_update(
/*
* Transaction states.
- * OPEN: The transaction is in a valid state and can accept new updates.
- * An OPEN transaction can be committed.
- * CLOSED: A closed transaction is no longer active and no other operations
- * than free can be used on it in this state.
- * A transaction can either become closed by successfully committing
- * an active transaction or if there is a failure while building
- * the transaction thus rendering it failed/inactive.
+ *
+ * OPEN: The transaction is initialized and new updates can still be
+ * added to it. An OPEN transaction can be prepared,
+ * committed, freed, or aborted (freeing and aborting an open
+ * transaction are equivalent).
+ *
+ * PREPARED: ref_transaction_prepare(), which locks all of the
+ * references involved in the update and checks that the
+ * update has no errors, has been called successfully for the
+ * transaction. A PREPARED transaction can be committed or
+ * aborted.
+ *
+ * CLOSED: The transaction is no longer active. A transaction becomes
+ * CLOSED if there is a failure while building the transaction
+ * or if a transaction is committed or aborted. A CLOSED
+ * transaction can only be freed.
*/
enum ref_transaction_state {
- REF_TRANSACTION_OPEN = 0,
- REF_TRANSACTION_CLOSED = 1
+ REF_TRANSACTION_OPEN = 0,
+ REF_TRANSACTION_PREPARED = 1,
+ REF_TRANSACTION_CLOSED = 2
};
/*
@@ -482,6 +500,10 @@ struct ref_store;
#define REF_STORE_WRITE (1 << 1) /* can perform update operations */
#define REF_STORE_ODB (1 << 2) /* has access to object database */
#define REF_STORE_MAIN (1 << 3)
+#define REF_STORE_ALL_CAPS (REF_STORE_READ | \
+ REF_STORE_WRITE | \
+ REF_STORE_ODB | \
+ REF_STORE_MAIN)
/*
* Initialize the ref_store for the specified gitdir. These functions
@@ -493,6 +515,18 @@ typedef struct ref_store *ref_store_init_fn(const char *gitdir,
typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
+typedef int ref_transaction_prepare_fn(struct ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err);
+
+typedef int ref_transaction_finish_fn(struct ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err);
+
+typedef int ref_transaction_abort_fn(struct ref_store *refs,
+ struct ref_transaction *transaction,
+ struct strbuf *err);
+
typedef int ref_transaction_commit_fn(struct ref_store *refs,
struct ref_transaction *transaction,
struct strbuf *err);
@@ -504,16 +538,17 @@ typedef int create_symref_fn(struct ref_store *ref_store,
const char *ref_target,
const char *refs_heads_master,
const char *logmsg);
-typedef int delete_refs_fn(struct ref_store *ref_store,
+typedef int delete_refs_fn(struct ref_store *ref_store, const char *msg,
struct string_list *refnames, unsigned int flags);
typedef int rename_ref_fn(struct ref_store *ref_store,
const char *oldref, const char *newref,
const char *logmsg);
/*
- * Iterate over the references in the specified ref_store that are
- * within find_containing_dir(prefix). If prefix is NULL or the empty
- * string, iterate over all references in the submodule.
+ * Iterate over the references in `ref_store` whose names start with
+ * `prefix`. `prefix` is matched as a literal string, without regard
+ * for path separators. If prefix is NULL or the empty string, iterate
+ * over all references in `ref_store`.
*/
typedef struct ref_iterator *ref_iterator_begin_fn(
struct ref_store *ref_store,
@@ -595,7 +630,10 @@ struct ref_storage_be {
const char *name;
ref_store_init_fn *init;
ref_init_db_fn *init_db;
- ref_transaction_commit_fn *transaction_commit;
+
+ ref_transaction_prepare_fn *transaction_prepare;
+ ref_transaction_finish_fn *transaction_finish;
+ ref_transaction_abort_fn *transaction_abort;
ref_transaction_commit_fn *initial_transaction_commit;
pack_refs_fn *pack_refs;
diff --git a/remote.c b/remote.c
index 32f137e966..3649d60cdc 100644
--- a/remote.c
+++ b/remote.c
@@ -477,26 +477,6 @@ static void read_config(void)
alias_all_urls();
}
-/*
- * This function frees a refspec array.
- * Warning: code paths should be checked to ensure that the src
- * and dst pointers are always freeable pointers as well
- * as the refspec pointer itself.
- */
-static void free_refspecs(struct refspec *refspec, int nr_refspec)
-{
- int i;
-
- if (!refspec)
- return;
-
- for (i = 0; i < nr_refspec; i++) {
- free(refspec[i].src);
- free(refspec[i].dst);
- }
- free(refspec);
-}
-
static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{
int i;
@@ -610,7 +590,7 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
* since it is only possible to reach this point from within
* the for loop above.
*/
- free_refspecs(rs, i+1);
+ free_refspec(i+1, rs);
return NULL;
}
die("Invalid refspec '%s'", refspec[i]);
@@ -621,7 +601,7 @@ int valid_fetch_refspec(const char *fetch_refspec_str)
struct refspec *refspec;
refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
- free_refspecs(refspec, 1);
+ free_refspec(1, refspec);
return !!refspec;
}
@@ -638,6 +618,10 @@ struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
void free_refspec(int nr_refspec, struct refspec *refspec)
{
int i;
+
+ if (!refspec)
+ return;
+
for (i = 0; i < nr_refspec; i++) {
free(refspec[i].src);
free(refspec[i].dst);
@@ -1302,7 +1286,7 @@ static void add_to_tips(struct tips *tips, const struct object_id *oid)
if (is_null_oid(oid))
return;
- commit = lookup_commit_reference_gently(oid->hash, 1);
+ commit = lookup_commit_reference_gently(oid, 1);
if (!commit || (commit->object.flags & TMP_MARK))
return;
commit->object.flags |= TMP_MARK;
@@ -1364,7 +1348,8 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
if (is_null_oid(&ref->new_oid))
continue;
- commit = lookup_commit_reference_gently(ref->new_oid.hash, 1);
+ commit = lookup_commit_reference_gently(&ref->new_oid,
+ 1);
if (!commit)
/* not pushing a commit, which is not an error */
continue;
@@ -1591,8 +1576,8 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
else if (!has_object_file(&ref->old_oid))
reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
- else if (!lookup_commit_reference_gently(ref->old_oid.hash, 1) ||
- !lookup_commit_reference_gently(ref->new_oid.hash, 1))
+ else if (!lookup_commit_reference_gently(&ref->old_oid, 1) ||
+ !lookup_commit_reference_gently(&ref->new_oid, 1))
reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
else if (!ref_newer(&ref->new_oid, &ref->old_oid))
reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
@@ -1959,12 +1944,12 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
* Both new and old must be commit-ish and new is descendant of
* old. Otherwise we require --force.
*/
- o = deref_tag(parse_object(old_oid->hash), NULL, 0);
+ o = deref_tag(parse_object(old_oid), NULL, 0);
if (!o || o->type != OBJ_COMMIT)
return 0;
old = (struct commit *) o;
- o = deref_tag(parse_object(new_oid->hash), NULL, 0);
+ o = deref_tag(parse_object(new_oid), NULL, 0);
if (!o || o->type != OBJ_COMMIT)
return 0;
new = (struct commit *) o;
@@ -2015,13 +2000,13 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
/* Cannot stat if what we used to build on no longer exists */
if (read_ref(base, oid.hash))
return -1;
- theirs = lookup_commit_reference(oid.hash);
+ theirs = lookup_commit_reference(&oid);
if (!theirs)
return -1;
if (read_ref(branch->refname, oid.hash))
return -1;
- ours = lookup_commit_reference(oid.hash);
+ ours = lookup_commit_reference(&oid);
if (!ours)
return -1;
diff --git a/revision.c b/revision.c
index 67ebc6fc4b..f88c14bab3 100644
--- a/revision.c
+++ b/revision.c
@@ -59,10 +59,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(entry.oid->hash));
+ mark_tree_uninteresting(lookup_tree(entry.oid));
break;
case OBJ_BLOB:
- mark_blob_uninteresting(lookup_blob(entry.oid->hash));
+ mark_blob_uninteresting(lookup_blob(entry.oid));
break;
default:
/* Subproject commit - not in this repository */
@@ -177,23 +177,23 @@ void add_pending_object(struct rev_info *revs,
void add_head_to_pending(struct rev_info *revs)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct object *obj;
- if (get_sha1("HEAD", sha1))
+ if (get_oid("HEAD", &oid))
return;
- obj = parse_object(sha1);
+ obj = parse_object(&oid);
if (!obj)
return;
add_pending_object(revs, obj, "HEAD");
}
static struct object *get_reference(struct rev_info *revs, const char *name,
- const unsigned char *sha1,
+ const struct object_id *oid,
unsigned int flags)
{
struct object *object;
- object = parse_object(sha1);
+ object = parse_object(oid);
if (!object) {
if (revs->ignore_missing)
return object;
@@ -203,10 +203,10 @@ static struct object *get_reference(struct rev_info *revs, const char *name,
return object;
}
-void add_pending_sha1(struct rev_info *revs, const char *name,
- const unsigned char *sha1, unsigned int flags)
+void add_pending_oid(struct rev_info *revs, const char *name,
+ const struct object_id *oid, unsigned int flags)
{
- struct object *object = get_reference(revs, name, sha1, flags);
+ struct object *object = get_reference(revs, name, oid, flags);
add_pending_object(revs, object, name);
}
@@ -228,7 +228,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(tag->tagged->oid.hash);
+ object = parse_object(&tag->tagged->oid);
if (!object) {
if (revs->ignore_missing_links || (flags & UNINTERESTING))
return NULL;
@@ -884,7 +884,7 @@ static void cherry_pick_list(struct commit_list *list, struct rev_info *revs)
/* How many extra uninteresting commits we want to see.. */
#define SLOP 5
-static int still_interesting(struct commit_list *src, unsigned long date, int slop,
+static int still_interesting(struct commit_list *src, timestamp_t date, int slop,
struct commit **interesting_cache)
{
/*
@@ -1018,7 +1018,7 @@ static void limit_left_right(struct commit_list *list, struct rev_info *revs)
static int limit_list(struct rev_info *revs)
{
int slop = SLOP;
- unsigned long date = ~0ul;
+ timestamp_t date = TIME_MAX;
struct commit_list *list = revs->commits;
struct commit_list *newlist = NULL;
struct commit_list **p = &newlist;
@@ -1157,9 +1157,9 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
if (ref_excluded(cb->all_revs->ref_excludes, path))
return 0;
- object = get_reference(cb->all_revs, path, oid->hash, cb->all_flags);
+ object = get_reference(cb->all_revs, path, oid, cb->all_flags);
add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
- add_pending_sha1(cb->all_revs, path, oid->hash, cb->all_flags);
+ add_pending_oid(cb->all_revs, path, oid, cb->all_flags);
return 0;
}
@@ -1200,7 +1200,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(oid->hash);
+ struct object *o = parse_object(oid);
if (o) {
o->flags |= cb->all_flags;
/* ??? CMDLINEFLAGS ??? */
@@ -1215,7 +1215,7 @@ static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
}
static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
handle_one_reflog_commit(ooid, cb_data);
@@ -1249,7 +1249,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(it->sha1);
+ struct tree *tree = lookup_tree(&it->oid);
add_pending_object_with_path(revs, &tree->object, "",
040000, path->buf);
}
@@ -1275,7 +1275,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
if (S_ISGITLINK(ce->ce_mode))
continue;
- blob = lookup_blob(ce->oid.hash);
+ blob = lookup_blob(&ce->oid);
if (!blob)
die("unable to add index blob to traversal");
add_pending_object_with_path(revs, &blob->object, "",
@@ -1292,7 +1292,7 @@ void add_index_objects_to_pending(struct rev_info *revs, unsigned flags)
static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
int exclude_parent)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct object *it;
struct commit *commit;
struct commit_list *parents;
@@ -1303,17 +1303,17 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags,
flags ^= UNINTERESTING | BOTTOM;
arg++;
}
- if (get_sha1_committish(arg, sha1))
+ if (get_sha1_committish(arg, oid.hash))
return 0;
while (1) {
- it = get_reference(revs, arg, sha1, 0);
+ it = get_reference(revs, arg, &oid, 0);
if (!it && revs->ignore_missing)
return 0;
if (it->type != OBJ_TAG)
break;
if (!((struct tag*)it)->tagged)
return 0;
- hashcpy(sha1, ((struct tag*)it)->tagged->oid.hash);
+ oidcpy(&oid, &((struct tag*)it)->tagged->oid);
}
if (it->type != OBJ_COMMIT)
return 0;
@@ -1389,16 +1389,16 @@ static void prepare_show_merge(struct rev_info *revs)
{
struct commit_list *bases;
struct commit *head, *other;
- unsigned char sha1[20];
+ struct object_id oid;
const char **prune = NULL;
int i, prune_num = 1; /* counting terminating NULL */
- if (get_sha1("HEAD", sha1))
+ if (get_oid("HEAD", &oid))
die("--merge without HEAD?");
- head = lookup_commit_or_die(sha1, "HEAD");
- if (get_sha1("MERGE_HEAD", sha1))
+ head = lookup_commit_or_die(&oid, "HEAD");
+ if (get_oid("MERGE_HEAD", &oid))
die("--merge without MERGE_HEAD?");
- other = lookup_commit_or_die(sha1, "MERGE_HEAD");
+ other = lookup_commit_or_die(&oid, "MERGE_HEAD");
add_pending_object(revs, &head->object, "HEAD");
add_pending_object(revs, &other->object, "MERGE_HEAD");
bases = get_merge_bases(head, other);
@@ -1429,134 +1429,168 @@ static void prepare_show_merge(struct rev_info *revs)
revs->limited = 1;
}
+static int dotdot_missing(const char *arg, char *dotdot,
+ struct rev_info *revs, int symmetric)
+{
+ if (revs->ignore_missing)
+ return 0;
+ /* de-munge so we report the full argument */
+ *dotdot = '.';
+ die(symmetric
+ ? "Invalid symmetric difference expression %s"
+ : "Invalid revision range %s", arg);
+}
+
+static int handle_dotdot_1(const char *arg, char *dotdot,
+ struct rev_info *revs, int flags,
+ int cant_be_filename,
+ struct object_context *a_oc,
+ struct object_context *b_oc)
+{
+ const char *a_name, *b_name;
+ struct object_id a_oid, b_oid;
+ struct object *a_obj, *b_obj;
+ unsigned int a_flags, b_flags;
+ int symmetric = 0;
+ unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
+ unsigned int oc_flags = GET_SHA1_COMMITTISH | GET_SHA1_RECORD_PATH;
+
+ a_name = arg;
+ if (!*a_name)
+ a_name = "HEAD";
+
+ b_name = dotdot + 2;
+ if (*b_name == '.') {
+ symmetric = 1;
+ b_name++;
+ }
+ if (!*b_name)
+ b_name = "HEAD";
+
+ if (get_sha1_with_context(a_name, oc_flags, a_oid.hash, a_oc) ||
+ get_sha1_with_context(b_name, oc_flags, b_oid.hash, b_oc))
+ return -1;
+
+ if (!cant_be_filename) {
+ *dotdot = '.';
+ verify_non_filename(revs->prefix, arg);
+ *dotdot = '\0';
+ }
+
+ a_obj = parse_object(&a_oid);
+ b_obj = parse_object(&b_oid);
+ if (!a_obj || !b_obj)
+ return dotdot_missing(arg, dotdot, revs, symmetric);
+
+ if (!symmetric) {
+ /* just A..B */
+ b_flags = flags;
+ a_flags = flags_exclude;
+ } else {
+ /* A...B -- find merge bases between the two */
+ struct commit *a, *b;
+ struct commit_list *exclude;
+
+ a = lookup_commit_reference(&a_obj->oid);
+ b = lookup_commit_reference(&b_obj->oid);
+ if (!a || !b)
+ return dotdot_missing(arg, dotdot, revs, symmetric);
+
+ exclude = get_merge_bases(a, b);
+ add_rev_cmdline_list(revs, exclude, REV_CMD_MERGE_BASE,
+ flags_exclude);
+ add_pending_commit_list(revs, exclude, flags_exclude);
+ free_commit_list(exclude);
+
+ b_flags = flags;
+ a_flags = flags | SYMMETRIC_LEFT;
+ }
+
+ a_obj->flags |= a_flags;
+ b_obj->flags |= b_flags;
+ add_rev_cmdline(revs, a_obj, a_name, REV_CMD_LEFT, a_flags);
+ add_rev_cmdline(revs, b_obj, b_name, REV_CMD_RIGHT, b_flags);
+ add_pending_object_with_path(revs, a_obj, a_name, a_oc->mode, a_oc->path);
+ add_pending_object_with_path(revs, b_obj, b_name, b_oc->mode, b_oc->path);
+ return 0;
+}
+
+static int handle_dotdot(const char *arg,
+ struct rev_info *revs, int flags,
+ int cant_be_filename)
+{
+ struct object_context a_oc, b_oc;
+ char *dotdot = strstr(arg, "..");
+ int ret;
+
+ if (!dotdot)
+ return -1;
+
+ memset(&a_oc, 0, sizeof(a_oc));
+ memset(&b_oc, 0, sizeof(b_oc));
+
+ *dotdot = '\0';
+ ret = handle_dotdot_1(arg, dotdot, revs, flags, cant_be_filename,
+ &a_oc, &b_oc);
+ *dotdot = '.';
+
+ free(a_oc.path);
+ free(b_oc.path);
+
+ return ret;
+}
+
int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsigned revarg_opt)
{
struct object_context oc;
- char *dotdot;
+ char *mark;
struct object *object;
- unsigned char sha1[20];
+ struct object_id oid;
int local_flags;
const char *arg = arg_;
int cant_be_filename = revarg_opt & REVARG_CANNOT_BE_FILENAME;
- unsigned get_sha1_flags = 0;
+ unsigned get_sha1_flags = GET_SHA1_RECORD_PATH;
flags = flags & UNINTERESTING ? flags | BOTTOM : flags & ~BOTTOM;
- dotdot = strstr(arg, "..");
- if (dotdot) {
- unsigned char from_sha1[20];
- const char *next = dotdot + 2;
- const char *this = arg;
- int symmetric = *next == '.';
- unsigned int flags_exclude = flags ^ (UNINTERESTING | BOTTOM);
- static const char head_by_default[] = "HEAD";
- unsigned int a_flags;
-
- *dotdot = 0;
- next += symmetric;
-
- if (!*next)
- next = head_by_default;
- if (dotdot == arg)
- this = head_by_default;
- if (this == head_by_default && next == head_by_default &&
- !symmetric) {
- /*
- * Just ".."? That is not a range but the
- * pathspec for the parent directory.
- */
- if (!cant_be_filename) {
- *dotdot = '.';
- return -1;
- }
- }
- if (!get_sha1_committish(this, from_sha1) &&
- !get_sha1_committish(next, sha1)) {
- struct object *a_obj, *b_obj;
-
- if (!cant_be_filename) {
- *dotdot = '.';
- verify_non_filename(revs->prefix, arg);
- }
-
- a_obj = parse_object(from_sha1);
- b_obj = parse_object(sha1);
- if (!a_obj || !b_obj) {
- missing:
- if (revs->ignore_missing)
- return 0;
- die(symmetric
- ? "Invalid symmetric difference expression %s"
- : "Invalid revision range %s", arg);
- }
-
- if (!symmetric) {
- /* just A..B */
- a_flags = flags_exclude;
- } else {
- /* A...B -- find merge bases between the two */
- struct commit *a, *b;
- struct commit_list *exclude;
-
- a = (a_obj->type == OBJ_COMMIT
- ? (struct commit *)a_obj
- : lookup_commit_reference(a_obj->oid.hash));
- b = (b_obj->type == OBJ_COMMIT
- ? (struct commit *)b_obj
- : lookup_commit_reference(b_obj->oid.hash));
- if (!a || !b)
- goto missing;
- exclude = get_merge_bases(a, b);
- add_rev_cmdline_list(revs, exclude,
- REV_CMD_MERGE_BASE,
- flags_exclude);
- add_pending_commit_list(revs, exclude,
- flags_exclude);
- free_commit_list(exclude);
-
- a_flags = flags | SYMMETRIC_LEFT;
- }
-
- a_obj->flags |= a_flags;
- b_obj->flags |= flags;
- add_rev_cmdline(revs, a_obj, this,
- REV_CMD_LEFT, a_flags);
- add_rev_cmdline(revs, b_obj, next,
- REV_CMD_RIGHT, flags);
- add_pending_object(revs, a_obj, this);
- add_pending_object(revs, b_obj, next);
- return 0;
- }
- *dotdot = '.';
+ if (!cant_be_filename && !strcmp(arg, "..")) {
+ /*
+ * Just ".."? That is not a range but the
+ * pathspec for the parent directory.
+ */
+ return -1;
}
- dotdot = strstr(arg, "^@");
- if (dotdot && !dotdot[2]) {
- *dotdot = 0;
+ if (!handle_dotdot(arg, revs, flags, revarg_opt))
+ return 0;
+
+ mark = strstr(arg, "^@");
+ if (mark && !mark[2]) {
+ *mark = 0;
if (add_parents_only(revs, arg, flags, 0))
return 0;
- *dotdot = '^';
+ *mark = '^';
}
- dotdot = strstr(arg, "^!");
- if (dotdot && !dotdot[2]) {
- *dotdot = 0;
+ mark = strstr(arg, "^!");
+ if (mark && !mark[2]) {
+ *mark = 0;
if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), 0))
- *dotdot = '^';
+ *mark = '^';
}
- dotdot = strstr(arg, "^-");
- if (dotdot) {
+ mark = strstr(arg, "^-");
+ if (mark) {
int exclude_parent = 1;
- if (dotdot[2]) {
+ if (mark[2]) {
char *end;
- exclude_parent = strtoul(dotdot + 2, &end, 10);
+ exclude_parent = strtoul(mark + 2, &end, 10);
if (*end != '\0' || !exclude_parent)
return -1;
}
- *dotdot = 0;
+ *mark = 0;
if (!add_parents_only(revs, arg, flags ^ (UNINTERESTING | BOTTOM), exclude_parent))
- *dotdot = '^';
+ *mark = '^';
}
local_flags = 0;
@@ -1566,15 +1600,16 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
}
if (revarg_opt & REVARG_COMMITTISH)
- get_sha1_flags = GET_SHA1_COMMITTISH;
+ get_sha1_flags |= GET_SHA1_COMMITTISH;
- if (get_sha1_with_context(arg, get_sha1_flags, sha1, &oc))
+ if (get_sha1_with_context(arg, get_sha1_flags, oid.hash, &oc))
return revs->ignore_missing ? 0 : -1;
if (!cant_be_filename)
verify_non_filename(revs->prefix, arg);
- object = get_reference(revs, arg, sha1, flags ^ local_flags);
+ object = get_reference(revs, arg, &oid, flags ^ local_flags);
add_rev_cmdline(revs, object, arg_, REV_CMD_REV, flags ^ local_flags);
- add_pending_object_with_mode(revs, object, arg, oc.mode);
+ add_pending_object_with_path(revs, object, arg, oc.mode, oc.path);
+ free(oc.path);
return 0;
}
@@ -1991,6 +2026,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
} else if (!strcmp(arg, "--extended-regexp") || !strcmp(arg, "-E")) {
revs->grep_filter.pattern_type_option = GREP_PATTERN_TYPE_ERE;
} else if (!strcmp(arg, "--regexp-ignore-case") || !strcmp(arg, "-i")) {
+ revs->grep_filter.ignore_case = 1;
revs->grep_filter.regflags |= REG_ICASE;
DIFF_OPT_SET(&revs->diffopt, PICKAXE_IGNORE_CASE);
} else if (!strcmp(arg, "--fixed-strings") || !strcmp(arg, "-F")) {
@@ -2287,12 +2323,12 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
if (revs->show_merge)
prepare_show_merge(revs);
if (revs->def && !revs->pending.nr && !got_rev_arg) {
- unsigned char sha1[20];
+ struct object_id oid;
struct object *object;
struct object_context oc;
- if (get_sha1_with_context(revs->def, 0, sha1, &oc))
+ if (get_sha1_with_context(revs->def, 0, oid.hash, &oc))
diagnose_missing_default(revs->def);
- object = get_reference(revs, revs->def, sha1, 0);
+ object = get_reference(revs, revs->def, &oid, 0);
add_pending_object_with_mode(revs, object, revs->def, oc.mode);
}
diff --git a/revision.h b/revision.h
index 14886ec92b..a91dd3d5d9 100644
--- a/revision.h
+++ b/revision.h
@@ -181,8 +181,8 @@ struct rev_info {
/* special limits */
int skip_count;
int max_count;
- unsigned long max_age;
- unsigned long min_age;
+ timestamp_t max_age;
+ timestamp_t min_age;
int min_parents;
int max_parents;
int (*include_check)(struct commit *, void *);
@@ -263,9 +263,9 @@ extern void show_object_with_name(FILE *, struct object *, const char *);
extern void add_pending_object(struct rev_info *revs,
struct object *obj, const char *name);
-extern void add_pending_sha1(struct rev_info *revs,
- const char *name, const unsigned char *sha1,
- unsigned int flags);
+extern void add_pending_oid(struct rev_info *revs,
+ const char *name, const struct object_id *oid,
+ unsigned int flags);
extern void add_head_to_pending(struct rev_info *);
extern void add_reflogs_to_pending(struct rev_info *, unsigned int flags);
diff --git a/run-command.c b/run-command.c
index 574b81d3e8..9e36151bf9 100644
--- a/run-command.c
+++ b/run-command.c
@@ -117,18 +117,65 @@ static inline void close_pair(int fd[2])
close(fd[1]);
}
-#ifndef GIT_WINDOWS_NATIVE
-static inline void dup_devnull(int to)
+int is_executable(const char *name)
{
- int fd = open("/dev/null", O_RDWR);
- if (fd < 0)
- die_errno(_("open /dev/null failed"));
- if (dup2(fd, to) < 0)
- die_errno(_("dup2(%d,%d) failed"), fd, to);
- close(fd);
+ struct stat st;
+
+ if (stat(name, &st) || /* stat, not lstat */
+ !S_ISREG(st.st_mode))
+ return 0;
+
+#if defined(GIT_WINDOWS_NATIVE)
+ /*
+ * On Windows there is no executable bit. The file extension
+ * indicates whether it can be run as an executable, and Git
+ * has special-handling to detect scripts and launch them
+ * through the indicated script interpreter. We test for the
+ * file extension first because virus scanners may make
+ * it quite expensive to open many files.
+ */
+ if (ends_with(name, ".exe"))
+ return S_IXUSR;
+
+{
+ /*
+ * Now that we know it does not have an executable extension,
+ * peek into the file instead.
+ */
+ char buf[3] = { 0 };
+ int n;
+ int fd = open(name, O_RDONLY);
+ st.st_mode &= ~S_IXUSR;
+ if (fd >= 0) {
+ n = read(fd, buf, 2);
+ if (n == 2)
+ /* look for a she-bang */
+ if (!strcmp(buf, "#!"))
+ st.st_mode |= S_IXUSR;
+ close(fd);
+ }
}
#endif
+ return st.st_mode & S_IXUSR;
+}
+/*
+ * Search $PATH for a command. This emulates the path search that
+ * execvp would perform, without actually executing the command so it
+ * can be used before fork() to prepare to run a command using
+ * execve() or after execvp() to diagnose why it failed.
+ *
+ * The caller should ensure that file contains no directory
+ * separators.
+ *
+ * Returns the path to the command, as found in $PATH or NULL if the
+ * command could not be found. The caller inherits ownership of the memory
+ * used to store the resultant path.
+ *
+ * This should not be used on Windows, where the $PATH search rules
+ * are more complicated (e.g., a search for "foo" should find
+ * "foo.exe").
+ */
static char *locate_in_PATH(const char *file)
{
const char *p = getenv("PATH");
@@ -149,7 +196,7 @@ static char *locate_in_PATH(const char *file)
}
strbuf_addstr(&buf, file);
- if (!access(buf.buf, F_OK))
+ if (is_executable(buf.buf))
return strbuf_detach(&buf, NULL);
if (!*end)
@@ -221,31 +268,248 @@ static const char **prepare_shell_cmd(struct argv_array *out, const char **argv)
}
#ifndef GIT_WINDOWS_NATIVE
-static int execv_shell_cmd(const char **argv)
+static int child_notifier = -1;
+
+enum child_errcode {
+ CHILD_ERR_CHDIR,
+ CHILD_ERR_DUP2,
+ CHILD_ERR_CLOSE,
+ CHILD_ERR_SIGPROCMASK,
+ CHILD_ERR_ENOENT,
+ CHILD_ERR_SILENT,
+ CHILD_ERR_ERRNO
+};
+
+struct child_err {
+ enum child_errcode err;
+ int syserr; /* errno */
+};
+
+static void child_die(enum child_errcode err)
{
- struct argv_array nargv = ARGV_ARRAY_INIT;
- prepare_shell_cmd(&nargv, argv);
- trace_argv_printf(nargv.argv, "trace: exec:");
- sane_execvp(nargv.argv[0], (char **)nargv.argv);
- argv_array_clear(&nargv);
- return -1;
+ struct child_err buf;
+
+ buf.err = err;
+ buf.syserr = errno;
+
+ /* write(2) on buf smaller than PIPE_BUF (min 512) is atomic: */
+ xwrite(child_notifier, &buf, sizeof(buf));
+ _exit(1);
}
-#endif
-#ifndef GIT_WINDOWS_NATIVE
-static int child_notifier = -1;
+static void child_dup2(int fd, int to)
+{
+ if (dup2(fd, to) < 0)
+ child_die(CHILD_ERR_DUP2);
+}
-static void notify_parent(void)
+static void child_close(int fd)
{
+ if (close(fd))
+ child_die(CHILD_ERR_CLOSE);
+}
+
+static void child_close_pair(int fd[2])
+{
+ child_close(fd[0]);
+ child_close(fd[1]);
+}
+
+/*
+ * parent will make it look like the child spewed a fatal error and died
+ * this is needed to prevent changes to t0061.
+ */
+static void fake_fatal(const char *err, va_list params)
+{
+ vreportf("fatal: ", err, params);
+}
+
+static void child_error_fn(const char *err, va_list params)
+{
+ const char msg[] = "error() should not be called in child\n";
+ xwrite(2, msg, sizeof(msg) - 1);
+}
+
+static void child_warn_fn(const char *err, va_list params)
+{
+ const char msg[] = "warn() should not be called in child\n";
+ xwrite(2, msg, sizeof(msg) - 1);
+}
+
+static void NORETURN child_die_fn(const char *err, va_list params)
+{
+ const char msg[] = "die() should not be called in child\n";
+ xwrite(2, msg, sizeof(msg) - 1);
+ _exit(2);
+}
+
+/* this runs in the parent process */
+static void child_err_spew(struct child_process *cmd, struct child_err *cerr)
+{
+ static void (*old_errfn)(const char *err, va_list params);
+
+ old_errfn = get_error_routine();
+ set_error_routine(fake_fatal);
+ errno = cerr->syserr;
+
+ switch (cerr->err) {
+ case CHILD_ERR_CHDIR:
+ error_errno("exec '%s': cd to '%s' failed",
+ cmd->argv[0], cmd->dir);
+ break;
+ case CHILD_ERR_DUP2:
+ error_errno("dup2() in child failed");
+ break;
+ case CHILD_ERR_CLOSE:
+ error_errno("close() in child failed");
+ break;
+ case CHILD_ERR_SIGPROCMASK:
+ error_errno("sigprocmask failed restoring signals");
+ break;
+ case CHILD_ERR_ENOENT:
+ error_errno("cannot run %s", cmd->argv[0]);
+ break;
+ case CHILD_ERR_SILENT:
+ break;
+ case CHILD_ERR_ERRNO:
+ error_errno("cannot exec '%s'", cmd->argv[0]);
+ break;
+ }
+ set_error_routine(old_errfn);
+}
+
+static void prepare_cmd(struct argv_array *out, const struct child_process *cmd)
+{
+ if (!cmd->argv[0])
+ die("BUG: command is empty");
+
+ /*
+ * Add SHELL_PATH so in the event exec fails with ENOEXEC we can
+ * attempt to interpret the command with 'sh'.
+ */
+ argv_array_push(out, SHELL_PATH);
+
+ if (cmd->git_cmd) {
+ argv_array_push(out, "git");
+ argv_array_pushv(out, cmd->argv);
+ } else if (cmd->use_shell) {
+ prepare_shell_cmd(out, cmd->argv);
+ } else {
+ argv_array_pushv(out, cmd->argv);
+ }
+
/*
- * execvp failed. If possible, we'd like to let start_command
- * know, so failures like ENOENT can be handled right away; but
- * otherwise, finish_command will still report the error.
+ * If there are no '/' characters in the command then perform a path
+ * lookup and use the resolved path as the command to exec. If there
+ * are no '/' characters or if the command wasn't found in the path,
+ * have exec attempt to invoke the command directly.
*/
- xwrite(child_notifier, "", 1);
+ if (!strchr(out->argv[1], '/')) {
+ char *program = locate_in_PATH(out->argv[1]);
+ if (program) {
+ free((char *)out->argv[1]);
+ out->argv[1] = program;
+ }
+ }
+}
+
+static char **prep_childenv(const char *const *deltaenv)
+{
+ extern char **environ;
+ char **childenv;
+ struct string_list env = STRING_LIST_INIT_DUP;
+ struct strbuf key = STRBUF_INIT;
+ const char *const *p;
+ int i;
+
+ /* Construct a sorted string list consisting of the current environ */
+ for (p = (const char *const *) environ; p && *p; p++) {
+ const char *equals = strchr(*p, '=');
+
+ if (equals) {
+ strbuf_reset(&key);
+ strbuf_add(&key, *p, equals - *p);
+ string_list_append(&env, key.buf)->util = (void *) *p;
+ } else {
+ string_list_append(&env, *p)->util = (void *) *p;
+ }
+ }
+ string_list_sort(&env);
+
+ /* Merge in 'deltaenv' with the current environ */
+ for (p = deltaenv; p && *p; p++) {
+ const char *equals = strchr(*p, '=');
+
+ if (equals) {
+ /* ('key=value'), insert or replace entry */
+ strbuf_reset(&key);
+ strbuf_add(&key, *p, equals - *p);
+ string_list_insert(&env, key.buf)->util = (void *) *p;
+ } else {
+ /* otherwise ('key') remove existing entry */
+ string_list_remove(&env, *p, 0);
+ }
+ }
+
+ /* Create an array of 'char *' to be used as the childenv */
+ childenv = xmalloc((env.nr + 1) * sizeof(char *));
+ for (i = 0; i < env.nr; i++)
+ childenv[i] = env.items[i].util;
+ childenv[env.nr] = NULL;
+
+ string_list_clear(&env, 0);
+ strbuf_release(&key);
+ return childenv;
+}
+
+struct atfork_state {
+#ifndef NO_PTHREADS
+ int cs;
+#endif
+ sigset_t old;
+};
+
+#ifndef NO_PTHREADS
+static void bug_die(int err, const char *msg)
+{
+ if (err) {
+ errno = err;
+ die_errno("BUG: %s", msg);
+ }
}
#endif
+static void atfork_prepare(struct atfork_state *as)
+{
+ sigset_t all;
+
+ if (sigfillset(&all))
+ die_errno("sigfillset");
+#ifdef NO_PTHREADS
+ if (sigprocmask(SIG_SETMASK, &all, &as->old))
+ die_errno("sigprocmask");
+#else
+ bug_die(pthread_sigmask(SIG_SETMASK, &all, &as->old),
+ "blocking all signals");
+ bug_die(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &as->cs),
+ "disabling cancellation");
+#endif
+}
+
+static void atfork_parent(struct atfork_state *as)
+{
+#ifdef NO_PTHREADS
+ if (sigprocmask(SIG_SETMASK, &as->old, NULL))
+ die_errno("sigprocmask");
+#else
+ bug_die(pthread_setcancelstate(as->cs, NULL),
+ "re-enabling cancellation");
+ bug_die(pthread_sigmask(SIG_SETMASK, &as->old, NULL),
+ "restoring signal mask");
+#endif
+}
+#endif /* GIT_WINDOWS_NATIVE */
+
static inline void set_cloexec(int fd)
{
int flags = fcntl(fd, F_GETFD);
@@ -281,13 +545,6 @@ static int wait_or_whine(pid_t pid, const char *argv0, int in_signal)
code += 128;
} else if (WIFEXITED(status)) {
code = WEXITSTATUS(status);
- /*
- * Convert special exit code when execvp failed.
- */
- if (code == 127) {
- code = -1;
- failed_errno = ENOENT;
- }
} else {
error("waitpid is confused (%s)", argv0);
}
@@ -372,109 +629,149 @@ fail_pipe:
#ifndef GIT_WINDOWS_NATIVE
{
int notify_pipe[2];
+ int null_fd = -1;
+ char **childenv;
+ struct argv_array argv = ARGV_ARRAY_INIT;
+ struct child_err cerr;
+ struct atfork_state as;
+
if (pipe(notify_pipe))
notify_pipe[0] = notify_pipe[1] = -1;
+ if (cmd->no_stdin || cmd->no_stdout || cmd->no_stderr) {
+ null_fd = open("/dev/null", O_RDWR | O_CLOEXEC);
+ if (null_fd < 0)
+ die_errno(_("open /dev/null failed"));
+ set_cloexec(null_fd);
+ }
+
+ prepare_cmd(&argv, cmd);
+ childenv = prep_childenv(cmd->env);
+ atfork_prepare(&as);
+
+ /*
+ * NOTE: In order to prevent deadlocking when using threads special
+ * care should be taken with the function calls made in between the
+ * fork() and exec() calls. No calls should be made to functions which
+ * require acquiring a lock (e.g. malloc) as the lock could have been
+ * held by another thread at the time of forking, causing the lock to
+ * never be released in the child process. This means only
+ * Async-Signal-Safe functions are permitted in the child.
+ */
cmd->pid = fork();
failed_errno = errno;
if (!cmd->pid) {
+ int sig;
/*
- * Redirect the channel to write syscall error messages to
- * before redirecting the process's stderr so that all die()
- * in subsequent call paths use the parent's stderr.
+ * Ensure the default die/error/warn routines do not get
+ * called, they can take stdio locks and malloc.
*/
- if (cmd->no_stderr || need_err) {
- int child_err = dup(2);
- set_cloexec(child_err);
- set_error_handle(fdopen(child_err, "w"));
- }
+ set_die_routine(child_die_fn);
+ set_error_routine(child_error_fn);
+ set_warn_routine(child_warn_fn);
close(notify_pipe[0]);
set_cloexec(notify_pipe[1]);
child_notifier = notify_pipe[1];
- atexit(notify_parent);
if (cmd->no_stdin)
- dup_devnull(0);
+ child_dup2(null_fd, 0);
else if (need_in) {
- dup2(fdin[0], 0);
- close_pair(fdin);
+ child_dup2(fdin[0], 0);
+ child_close_pair(fdin);
} else if (cmd->in) {
- dup2(cmd->in, 0);
- close(cmd->in);
+ child_dup2(cmd->in, 0);
+ child_close(cmd->in);
}
if (cmd->no_stderr)
- dup_devnull(2);
+ child_dup2(null_fd, 2);
else if (need_err) {
- dup2(fderr[1], 2);
- close_pair(fderr);
+ child_dup2(fderr[1], 2);
+ child_close_pair(fderr);
} else if (cmd->err > 1) {
- dup2(cmd->err, 2);
- close(cmd->err);
+ child_dup2(cmd->err, 2);
+ child_close(cmd->err);
}
if (cmd->no_stdout)
- dup_devnull(1);
+ child_dup2(null_fd, 1);
else if (cmd->stdout_to_stderr)
- dup2(2, 1);
+ child_dup2(2, 1);
else if (need_out) {
- dup2(fdout[1], 1);
- close_pair(fdout);
+ child_dup2(fdout[1], 1);
+ child_close_pair(fdout);
} else if (cmd->out > 1) {
- dup2(cmd->out, 1);
- close(cmd->out);
+ child_dup2(cmd->out, 1);
+ child_close(cmd->out);
}
if (cmd->dir && chdir(cmd->dir))
- die_errno("exec '%s': cd to '%s' failed", cmd->argv[0],
- cmd->dir);
- if (cmd->env) {
- for (; *cmd->env; cmd->env++) {
- if (strchr(*cmd->env, '='))
- putenv((char *)*cmd->env);
- else
- unsetenv(*cmd->env);
- }
+ child_die(CHILD_ERR_CHDIR);
+
+ /*
+ * restore default signal handlers here, in case
+ * we catch a signal right before execve below
+ */
+ for (sig = 1; sig < NSIG; sig++) {
+ /* ignored signals get reset to SIG_DFL on execve */
+ if (signal(sig, SIG_DFL) == SIG_IGN)
+ signal(sig, SIG_IGN);
}
- if (cmd->git_cmd)
- execv_git_cmd(cmd->argv);
- else if (cmd->use_shell)
- execv_shell_cmd(cmd->argv);
- else
- sane_execvp(cmd->argv[0], (char *const*) cmd->argv);
+
+ if (sigprocmask(SIG_SETMASK, &as.old, NULL) != 0)
+ child_die(CHILD_ERR_SIGPROCMASK);
+
+ /*
+ * Attempt to exec using the command and arguments starting at
+ * argv.argv[1]. argv.argv[0] contains SHELL_PATH which will
+ * be used in the event exec failed with ENOEXEC at which point
+ * we will try to interpret the command using 'sh'.
+ */
+ execve(argv.argv[1], (char *const *) argv.argv + 1,
+ (char *const *) childenv);
+ if (errno == ENOEXEC)
+ execve(argv.argv[0], (char *const *) argv.argv,
+ (char *const *) childenv);
+
if (errno == ENOENT) {
- if (!cmd->silent_exec_failure)
- error("cannot run %s: %s", cmd->argv[0],
- strerror(ENOENT));
- exit(127);
+ if (cmd->silent_exec_failure)
+ child_die(CHILD_ERR_SILENT);
+ child_die(CHILD_ERR_ENOENT);
} else {
- die_errno("cannot exec '%s'", cmd->argv[0]);
+ child_die(CHILD_ERR_ERRNO);
}
}
+ atfork_parent(&as);
if (cmd->pid < 0)
error_errno("cannot fork() for %s", cmd->argv[0]);
else if (cmd->clean_on_exit)
mark_child_for_cleanup(cmd->pid, cmd);
/*
- * Wait for child's execvp. If the execvp succeeds (or if fork()
+ * Wait for child's exec. If the exec succeeds (or if fork()
* failed), EOF is seen immediately by the parent. Otherwise, the
- * child process sends a single byte.
+ * child process sends a child_err struct.
* Note that use of this infrastructure is completely advisory,
* therefore, we keep error checks minimal.
*/
close(notify_pipe[1]);
- if (read(notify_pipe[0], &notify_pipe[1], 1) == 1) {
+ if (xread(notify_pipe[0], &cerr, sizeof(cerr)) == sizeof(cerr)) {
/*
- * At this point we know that fork() succeeded, but execvp()
+ * At this point we know that fork() succeeded, but exec()
* failed. Errors have been reported to our stderr.
*/
wait_or_whine(cmd->pid, cmd->argv[0], 0);
+ child_err_spew(cmd, &cerr);
failed_errno = errno;
cmd->pid = -1;
}
close(notify_pipe[0]);
+
+ if (null_fd >= 0)
+ close(null_fd);
+ argv_array_clear(&argv);
+ free(childenv);
}
#else
{
diff --git a/run-command.h b/run-command.h
index 4fa8f65adb..3932420ec8 100644
--- a/run-command.h
+++ b/run-command.h
@@ -51,6 +51,7 @@ struct child_process {
#define CHILD_PROCESS_INIT { NULL, ARGV_ARRAY_INIT, ARGV_ARRAY_INIT }
void child_process_init(struct child_process *);
void child_process_clear(struct child_process *);
+extern int is_executable(const char *name);
int start_command(struct child_process *);
int finish_command(struct child_process *);
diff --git a/sequencer.c b/sequencer.c
index 8c72cf1614..924fb1d0c3 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -344,7 +344,7 @@ static int read_oneliner(struct strbuf *buf,
static struct tree *empty_tree(void)
{
- return lookup_tree(EMPTY_TREE_SHA1_BIN);
+ return lookup_tree(&empty_tree_oid);
}
static int error_dirty_index(struct replay_opts *opts)
@@ -374,7 +374,7 @@ static void update_abort_safety_file(void)
write_file(git_path_abort_safety_file(), "%s", "");
}
-static int fast_forward_to(const unsigned char *to, const unsigned char *from,
+static int fast_forward_to(const struct object_id *to, const struct object_id *from,
int unborn, struct replay_opts *opts)
{
struct ref_transaction *transaction;
@@ -390,7 +390,7 @@ static int fast_forward_to(const unsigned char *to, const unsigned char *from,
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_update(transaction, "HEAD",
- to, unborn ? null_sha1 : from,
+ to->hash, unborn ? null_sha1 : from->hash,
0, sb.buf, &err) ||
ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
@@ -426,7 +426,7 @@ void append_conflicts_hint(struct strbuf *msgbuf)
static int do_recursive_merge(struct commit *base, struct commit *next,
const char *base_label, const char *next_label,
- unsigned char *head, struct strbuf *msgbuf,
+ struct object_id *head, struct strbuf *msgbuf,
struct replay_opts *opts)
{
struct merge_options o;
@@ -483,13 +483,13 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
static int is_index_unchanged(void)
{
- unsigned char head_sha1[20];
+ struct object_id head_oid;
struct commit *head_commit;
- if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_sha1, NULL))
+ if (!resolve_ref_unsafe("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL))
return error(_("could not resolve HEAD commit\n"));
- head_commit = lookup_commit(head_sha1);
+ head_commit = lookup_commit(&head_oid);
/*
* If head_commit is NULL, check_commit, called from
@@ -509,7 +509,8 @@ static int is_index_unchanged(void)
if (cache_tree_update(&the_index, 0))
return error(_("unable to update cache tree\n"));
- return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);
+ return !oidcmp(&active_cache_tree->oid,
+ &head_commit->tree->object.oid);
}
static int write_author_script(const char *message)
@@ -835,13 +836,13 @@ static int update_squash_messages(enum todo_command command,
strbuf_splice(&buf, 0, eol - buf.buf, header.buf, header.len);
strbuf_release(&header);
} else {
- unsigned char head[20];
+ struct object_id head;
struct commit *head_commit;
const char *head_message, *body;
- if (get_sha1("HEAD", head))
+ if (get_oid("HEAD", &head))
return error(_("need a HEAD to fixup"));
- if (!(head_commit = lookup_commit_reference(head)))
+ if (!(head_commit = lookup_commit_reference(&head)))
return error(_("could not read HEAD"));
if (!(head_message = get_commit_buffer(head_commit, NULL)))
return error(_("could not read HEAD's commit message"));
@@ -935,7 +936,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
{
unsigned int flags = opts->edit ? EDIT_MSG : 0;
const char *msg_file = opts->edit ? NULL : git_path_merge_msg();
- unsigned char head[20];
+ struct object_id head;
struct commit *base, *next, *parent;
const char *base_label, *next_label;
struct commit_message msg = { NULL, NULL, NULL, NULL };
@@ -949,12 +950,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
* that represents the "current" state for merge-recursive
* to work on.
*/
- if (write_cache_as_tree(head, 0, NULL))
+ if (write_cache_as_tree(head.hash, 0, NULL))
return error(_("your index file is unmerged."));
} else {
- unborn = get_sha1("HEAD", head);
+ unborn = get_oid("HEAD", &head);
if (unborn)
- hashcpy(head, EMPTY_TREE_SHA1_BIN);
+ oidcpy(&head, &empty_tree_oid);
if (index_differs_from(unborn ? EMPTY_TREE_SHA1_HEX : "HEAD", 0, 0))
return error_dirty_index(opts);
}
@@ -990,11 +991,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
oid_to_hex(&commit->object.oid));
if (opts->allow_ff && !is_fixup(command) &&
- ((parent && !hashcmp(parent->object.oid.hash, head)) ||
+ ((parent && !oidcmp(&parent->object.oid, &head)) ||
(!parent && unborn))) {
if (is_rebase_i(opts))
write_author_script(msg.message);
- res = fast_forward_to(commit->object.oid.hash, head, unborn,
+ res = fast_forward_to(&commit->object.oid, &head, unborn,
opts);
if (res || command != TODO_REWORD)
goto leave;
@@ -1082,7 +1083,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
res = -1;
else if (!opts->strategy || !strcmp(opts->strategy, "recursive") || command == TODO_REVERT) {
res = do_recursive_merge(base, next, base_label, next_label,
- head, &msgbuf, opts);
+ &head, &msgbuf, opts);
if (res < 0)
return res;
res |= write_message(msgbuf.buf, msgbuf.len,
@@ -1098,7 +1099,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
commit_list_insert(next, &remotes);
res |= try_merge_command(opts->strategy,
opts->xopts_nr, (const char **)opts->xopts,
- common, sha1_to_hex(head), remotes);
+ common, oid_to_hex(&head), remotes);
free_commit_list(common);
free_commit_list(remotes);
}
@@ -1223,7 +1224,7 @@ static struct todo_item *append_new_todo(struct todo_list *todo_list)
static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
{
- unsigned char commit_sha1[20];
+ struct object_id commit_oid;
char *end_of_object_name;
int i, saved, status, padding;
@@ -1272,7 +1273,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
end_of_object_name = (char *) bol + strcspn(bol, " \t\n");
saved = *end_of_object_name;
*end_of_object_name = '\0';
- status = get_sha1(bol, commit_sha1);
+ status = get_oid(bol, &commit_oid);
*end_of_object_name = saved;
item->arg = end_of_object_name + strspn(end_of_object_name, " \t");
@@ -1281,7 +1282,7 @@ static int parse_insn_line(struct todo_item *item, const char *bol, char *eol)
if (status < 0)
return -1;
- item->commit = lookup_commit_reference(commit_sha1);
+ item->commit = lookup_commit_reference(&commit_oid);
return !item->commit;
}
@@ -2285,7 +2286,7 @@ static int single_pick(struct commit *cmit, struct replay_opts *opts)
int sequencer_pick_revisions(struct replay_opts *opts)
{
struct todo_list todo_list = TODO_LIST_INIT;
- unsigned char sha1[20];
+ struct object_id oid;
int i, res;
assert(opts->revs);
@@ -2293,16 +2294,16 @@ int sequencer_pick_revisions(struct replay_opts *opts)
return -1;
for (i = 0; i < opts->revs->pending.nr; i++) {
- unsigned char sha1[20];
+ struct object_id oid;
const char *name = opts->revs->pending.objects[i].name;
/* This happens when using --stdin. */
if (!strlen(name))
continue;
- if (!get_sha1(name, sha1)) {
- if (!lookup_commit_reference_gently(sha1, 1)) {
- enum object_type type = sha1_object_info(sha1, NULL);
+ if (!get_oid(name, &oid)) {
+ if (!lookup_commit_reference_gently(&oid, 1)) {
+ enum object_type type = sha1_object_info(oid.hash, NULL);
return error(_("%s: can't cherry-pick a %s"),
name, typename(type));
}
@@ -2339,9 +2340,9 @@ int sequencer_pick_revisions(struct replay_opts *opts)
if (walk_revs_populate_todo(&todo_list, opts) ||
create_seq_dir() < 0)
return -1;
- if (get_sha1("HEAD", sha1) && (opts->action == REPLAY_REVERT))
+ if (get_oid("HEAD", &oid) && (opts->action == REPLAY_REVERT))
return error(_("can't revert as initial commit"));
- if (save_head(sha1_to_hex(sha1)))
+ if (save_head(oid_to_hex(&oid)))
return -1;
if (save_opts(opts))
return -1;
diff --git a/server-info.c b/server-info.c
index f6c1a3dfb0..6f865b73a3 100644
--- a/server-info.c
+++ b/server-info.c
@@ -53,7 +53,7 @@ static int add_info_ref(const char *path, const struct object_id *oid,
int flag, void *cb_data)
{
FILE *fp = cb_data;
- struct object *o = parse_object(oid->hash);
+ struct object *o = parse_object(oid);
if (!o)
return -1;
diff --git a/sha1_name.c b/sha1_name.c
index 8eec9f7c1b..e9ffe685d5 100644
--- a/sha1_name.c
+++ b/sha1_name.c
@@ -241,7 +241,7 @@ static int disambiguate_committish_only(const struct object_id *oid, void *cb_da
return 0;
/* We need to do this the hard way... */
- obj = deref_tag(parse_object(oid->hash), NULL, 0);
+ obj = deref_tag(parse_object(oid), NULL, 0);
if (obj && obj->type == OBJ_COMMIT)
return 1;
return 0;
@@ -265,7 +265,7 @@ static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_
return 0;
/* We need to do this the hard way... */
- obj = deref_tag(parse_object(oid->hash), NULL, 0);
+ obj = deref_tag(parse_object(oid), NULL, 0);
if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
return 1;
return 0;
@@ -354,14 +354,14 @@ static int show_ambiguous_object(const struct object_id *oid, void *data)
type = sha1_object_info(oid->hash, NULL);
if (type == OBJ_COMMIT) {
- struct commit *commit = lookup_commit(oid->hash);
+ struct commit *commit = lookup_commit(oid);
if (commit) {
struct pretty_print_context pp = {0};
pp.date_mode.type = DATE_SHORT;
format_commit_message(commit, " %ad - %s", &desc, &pp);
}
} else if (type == OBJ_TAG) {
- struct tag *tag = lookup_tag(oid->hash);
+ struct tag *tag = lookup_tag(oid);
if (!parse_tag(tag) && tag->tag)
strbuf_addf(&desc, " %s", tag->tag);
}
@@ -660,8 +660,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
if (reflog_len) {
int nth, i;
- unsigned long at_time;
- unsigned long co_time;
+ timestamp_t at_time;
+ timestamp_t co_time;
int co_tz, co_cnt;
/* Is it asking for N-th entry, or approxidate? */
@@ -722,14 +722,14 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1,
static int get_parent(const char *name, int len,
unsigned char *result, int idx)
{
- unsigned char sha1[20];
- int ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
+ struct object_id oid;
+ int ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
struct commit *commit;
struct commit_list *p;
if (ret)
return ret;
- commit = lookup_commit_reference(sha1);
+ commit = lookup_commit_reference(&oid);
if (parse_commit(commit))
return -1;
if (!idx) {
@@ -750,14 +750,14 @@ static int get_parent(const char *name, int len,
static int get_nth_ancestor(const char *name, int len,
unsigned char *result, int generation)
{
- unsigned char sha1[20];
+ struct object_id oid;
struct commit *commit;
int ret;
- ret = get_sha1_1(name, len, sha1, GET_SHA1_COMMITTISH);
+ ret = get_sha1_1(name, len, oid.hash, GET_SHA1_COMMITTISH);
if (ret)
return ret;
- commit = lookup_commit_reference(sha1);
+ commit = lookup_commit_reference(&oid);
if (!commit)
return -1;
@@ -776,7 +776,7 @@ struct object *peel_to_type(const char *name, int namelen,
if (name && !namelen)
namelen = strlen(name);
while (1) {
- if (!o || (!o->parsed && !parse_object(o->oid.hash)))
+ if (!o || (!o->parsed && !parse_object(&o->oid)))
return NULL;
if (expected_type == OBJ_ANY || o->type == expected_type)
return o;
@@ -798,7 +798,7 @@ struct object *peel_to_type(const char *name, int namelen,
static int peel_onion(const char *name, int len, unsigned char *sha1,
unsigned lookup_flags)
{
- unsigned char outer[20];
+ struct object_id outer;
const char *sp;
unsigned int expected_type = 0;
struct object *o;
@@ -846,15 +846,15 @@ static int peel_onion(const char *name, int len, unsigned char *sha1,
else if (expected_type == OBJ_TREE)
lookup_flags |= GET_SHA1_TREEISH;
- if (get_sha1_1(name, sp - name - 2, outer, lookup_flags))
+ if (get_sha1_1(name, sp - name - 2, outer.hash, lookup_flags))
return -1;
- o = parse_object(outer);
+ o = parse_object(&outer);
if (!o)
return -1;
if (!expected_type) {
o = deref_tag(o, name, sp - name - 2);
- if (!o || (!o->parsed && !parse_object(o->oid.hash)))
+ if (!o || (!o->parsed && !parse_object(&o->oid)))
return -1;
hashcpy(sha1, o->oid.hash);
return 0;
@@ -981,7 +981,7 @@ static int handle_one_ref(const char *path, const struct object_id *oid,
int flag, void *cb_data)
{
struct commit_list **list = cb_data;
- struct object *object = parse_object(oid->hash);
+ struct object *object = parse_object(oid);
if (!object)
return 0;
if (object->type == OBJ_TAG) {
@@ -1027,7 +1027,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
int matches;
commit = pop_most_recent_commit(&list, ONELINE_SEEN);
- if (!parse_object(commit->object.oid.hash))
+ if (!parse_object(&commit->object.oid))
continue;
buf = get_commit_buffer(commit, NULL);
p = strstr(buf, "\n\n");
@@ -1054,7 +1054,7 @@ struct grab_nth_branch_switch_cbdata {
};
static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct grab_nth_branch_switch_cbdata *cb = cb_data;
@@ -1136,13 +1136,13 @@ int get_oid_mb(const char *name, struct object_id *oid)
}
if (st)
return st;
- one = lookup_commit_reference_gently(oid_tmp.hash, 0);
+ one = lookup_commit_reference_gently(&oid_tmp, 0);
if (!one)
return -1;
if (get_sha1_committish(dots[3] ? (dots + 3) : "HEAD", oid_tmp.hash))
return -1;
- two = lookup_commit_reference_gently(oid_tmp.hash, 0);
+ two = lookup_commit_reference_gently(&oid_tmp, 0);
if (!two)
return -1;
mbs = get_merge_bases(one, two);
@@ -1511,6 +1511,7 @@ static int get_sha1_with_context_1(const char *name,
memset(oc, 0, sizeof(*oc));
oc->mode = S_IFINVALID;
+ strbuf_init(&oc->symlink_path, 0);
ret = get_sha1_1(name, namelen, sha1, flags);
if (!ret)
return ret;
@@ -1549,7 +1550,8 @@ static int get_sha1_with_context_1(const char *name,
namelen = strlen(cp);
}
- strlcpy(oc->path, cp, sizeof(oc->path));
+ if (flags & GET_SHA1_RECORD_PATH)
+ oc->path = xstrdup(cp);
if (!active_cache)
read_cache();
@@ -1612,7 +1614,8 @@ static int get_sha1_with_context_1(const char *name,
}
}
hashcpy(oc->tree, tree_sha1);
- strlcpy(oc->path, filename, sizeof(oc->path));
+ if (flags & GET_SHA1_RECORD_PATH)
+ oc->path = xstrdup(filename);
free(new_filename);
return ret;
@@ -1638,9 +1641,9 @@ void maybe_die_on_misspelt_object_name(const char *name, const char *prefix)
get_sha1_with_context_1(name, GET_SHA1_ONLY_TO_DIE, prefix, sha1, &oc);
}
-int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *orc)
+int get_sha1_with_context(const char *str, unsigned flags, unsigned char *sha1, struct object_context *oc)
{
if (flags & GET_SHA1_FOLLOW_SYMLINKS && flags & GET_SHA1_ONLY_TO_DIE)
die("BUG: incompatible flags for get_sha1_with_context");
- return get_sha1_with_context_1(str, flags, NULL, sha1, orc);
+ return get_sha1_with_context_1(str, flags, NULL, sha1, oc);
}
diff --git a/shallow.c b/shallow.c
index f9370961f9..ef7ca78993 100644
--- a/shallow.c
+++ b/shallow.c
@@ -27,13 +27,13 @@ void set_alternate_shallow_file(const char *path, int override)
alternate_shallow_file = xstrdup_or_null(path);
}
-int register_shallow(const unsigned char *sha1)
+int register_shallow(const struct object_id *oid)
{
struct commit_graft *graft =
xmalloc(sizeof(struct commit_graft));
- struct commit *commit = lookup_commit(sha1);
+ struct commit *commit = lookup_commit(oid);
- hashcpy(graft->oid.hash, sha1);
+ oidcpy(&graft->oid, oid);
graft->nr_parent = -1;
if (commit && commit->object.parsed)
commit->parents = NULL;
@@ -65,10 +65,10 @@ int is_repository_shallow(void)
is_shallow = 1;
while (fgets(buf, sizeof(buf), fp)) {
- unsigned char sha1[20];
- if (get_sha1_hex(buf, sha1))
+ struct object_id oid;
+ if (get_oid_hex(buf, &oid))
die("bad shallow line: %s", buf);
- register_shallow(sha1);
+ register_shallow(&oid);
}
fclose(fp);
return is_shallow;
@@ -241,7 +241,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
if (graft->nr_parent != -1)
return 0;
if (data->flags & SEEN_ONLY) {
- struct commit *c = lookup_commit(graft->oid.hash);
+ struct commit *c = lookup_commit(&graft->oid);
if (!c || !(c->object.flags & SEEN)) {
if (data->flags & VERBOSE)
printf("Removing %s from .git/shallow\n",
@@ -466,14 +466,14 @@ static uint32_t *paint_alloc(struct paint_info *info)
* UNINTERESTING or BOTTOM is hit. Set the id-th bit in ref_bitmap for
* all walked commits.
*/
-static void paint_down(struct paint_info *info, const unsigned char *sha1,
+static void paint_down(struct paint_info *info, const struct object_id *oid,
unsigned int id)
{
unsigned int i, nr;
struct commit_list *head = NULL;
int bitmap_nr = (info->nr_bits + 31) / 32;
size_t bitmap_size = st_mult(sizeof(uint32_t), bitmap_nr);
- struct commit *c = lookup_commit_reference_gently(sha1, 1);
+ struct commit *c = lookup_commit_reference_gently(oid, 1);
uint32_t *tmp; /* to be freed before return */
uint32_t *bitmap;
@@ -535,7 +535,7 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1,
static int mark_uninteresting(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
- struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
+ struct commit *commit = lookup_commit_reference_gently(oid, 1);
if (!commit)
return 0;
commit->object.flags |= UNINTERESTING;
@@ -603,18 +603,18 @@ void assign_shallow_commits_to_refs(struct shallow_info *info,
/* Mark potential bottoms so we won't go out of bound */
for (i = 0; i < nr_shallow; i++) {
- struct commit *c = lookup_commit(oid[shallow[i]].hash);
+ struct commit *c = lookup_commit(&oid[shallow[i]]);
c->object.flags |= BOTTOM;
}
for (i = 0; i < ref->nr; i++)
- paint_down(&pi, ref->oid[i].hash, i);
+ paint_down(&pi, ref->oid + i, i);
if (used) {
int bitmap_size = ((pi.nr_bits + 31) / 32) * sizeof(uint32_t);
memset(used, 0, sizeof(*used) * info->shallow->nr);
for (i = 0; i < nr_shallow; i++) {
- const struct commit *c = lookup_commit(oid[shallow[i]].hash);
+ const struct commit *c = lookup_commit(&oid[shallow[i]]);
uint32_t **map = ref_bitmap_at(&pi.ref_bitmap, c);
if (*map)
used[shallow[i]] = xmemdupz(*map, bitmap_size);
@@ -645,7 +645,7 @@ static int add_ref(const char *refname, const struct object_id *oid,
{
struct commit_array *ca = cb_data;
ALLOC_GROW(ca->commits, ca->nr + 1, ca->alloc);
- ca->commits[ca->nr] = lookup_commit_reference_gently(oid->hash, 1);
+ ca->commits[ca->nr] = lookup_commit_reference_gently(oid, 1);
if (ca->commits[ca->nr])
ca->nr++;
return 0;
@@ -683,7 +683,7 @@ static void post_assign_shallow(struct shallow_info *info,
for (i = dst = 0; i < info->nr_theirs; i++) {
if (i != dst)
info->theirs[dst] = info->theirs[i];
- c = lookup_commit(oid[info->theirs[i]].hash);
+ c = lookup_commit(&oid[info->theirs[i]]);
bitmap = ref_bitmap_at(ref_bitmap, c);
if (!*bitmap)
continue;
@@ -704,7 +704,7 @@ static void post_assign_shallow(struct shallow_info *info,
for (i = dst = 0; i < info->nr_ours; i++) {
if (i != dst)
info->ours[dst] = info->ours[i];
- c = lookup_commit(oid[info->ours[i]].hash);
+ c = lookup_commit(&oid[info->ours[i]]);
bitmap = ref_bitmap_at(ref_bitmap, c);
if (!*bitmap)
continue;
@@ -726,7 +726,7 @@ static void post_assign_shallow(struct shallow_info *info,
int delayed_reachability_test(struct shallow_info *si, int c)
{
if (si->need_reachability_test[c]) {
- struct commit *commit = lookup_commit(si->shallow->oid[c].hash);
+ struct commit *commit = lookup_commit(&si->shallow->oid[c]);
if (!si->commits) {
struct commit_array ca;
diff --git a/split-index.c b/split-index.c
index f519e60f87..49bd197f71 100644
--- a/split-index.c
+++ b/split-index.c
@@ -73,10 +73,17 @@ void move_cache_to_base_index(struct index_state *istate)
int i;
/*
- * do not delete old si->base, its index entries may be shared
- * with istate->cache[]. Accept a bit of leaking here because
- * this code is only used by short-lived update-index.
+ * If "si" is shared with another index_state (e.g. by
+ * unpack-trees code), we will need to duplicate split_index
+ * struct. It's not happening now though, luckily.
*/
+ assert(si->refcount <= 1);
+
+ unshare_split_index(istate, 0);
+ if (si->base) {
+ discard_index(si->base);
+ free(si->base);
+ }
si->base = xcalloc(1, sizeof(*si->base));
si->base->version = istate->version;
/* zero timestamp disables racy test in ce_write_index() */
@@ -275,11 +282,41 @@ void finish_writing_split_index(struct index_state *istate)
istate->cache_nr = si->saved_cache_nr;
}
+void unshare_split_index(struct index_state *istate, int discard)
+{
+ struct split_index *si = istate->split_index;
+ int i;
+
+ if (!si || !si->base)
+ return;
+
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
+ struct cache_entry *new = NULL;
+
+ if (!ce->index ||
+ ce->index > si->base->cache_nr ||
+ ce != si->base->cache[ce->index - 1])
+ continue;
+
+ if (!discard) {
+ int len = ce_namelen(ce);
+ new = xcalloc(1, cache_entry_size(len));
+ copy_cache_entry(new, ce);
+ memcpy(new->name, ce->name, len);
+ new->index = 0;
+ }
+ istate->cache[i] = new;
+ }
+}
+
+
void discard_split_index(struct index_state *istate)
{
struct split_index *si = istate->split_index;
if (!si)
return;
+ unshare_split_index(istate, 0);
istate->split_index = NULL;
si->refcount--;
if (si->refcount)
@@ -328,14 +365,8 @@ void add_split_index(struct index_state *istate)
void remove_split_index(struct index_state *istate)
{
- if (istate->split_index) {
- /*
- * can't discard_split_index(&the_index); because that
- * will destroy split_index->base->cache[], which may
- * be shared with the_index.cache[]. So yeah we're
- * leaking a bit here.
- */
- istate->split_index = NULL;
- istate->cache_changed |= SOMETHING_CHANGED;
- }
+ if (!istate->split_index)
+ return;
+ discard_split_index(istate);
+ istate->cache_changed |= SOMETHING_CHANGED;
}
diff --git a/split-index.h b/split-index.h
index df91c1bda8..65c0f09b2b 100644
--- a/split-index.h
+++ b/split-index.h
@@ -33,5 +33,6 @@ void finish_writing_split_index(struct index_state *istate);
void discard_split_index(struct index_state *istate);
void add_split_index(struct index_state *istate);
void remove_split_index(struct index_state *istate);
+void unshare_split_index(struct index_state *istate, int discard);
#endif
diff --git a/string-list.c b/string-list.c
index 003ca1879e..c650500c6e 100644
--- a/string-list.c
+++ b/string-list.c
@@ -64,6 +64,24 @@ struct string_list_item *string_list_insert(struct string_list *list, const char
return list->items + index;
}
+void string_list_remove(struct string_list *list, const char *string,
+ int free_util)
+{
+ int exact_match;
+ int i = get_entry_index(list, string, &exact_match);
+
+ if (exact_match) {
+ if (list->strdup_strings)
+ free(list->items[i].string);
+ if (free_util)
+ free(list->items[i].util);
+
+ list->nr--;
+ memmove(list->items + i, list->items + i + 1,
+ (list->nr - i) * sizeof(struct string_list_item));
+ }
+}
+
int string_list_has_string(const struct string_list *list, const char *string)
{
int exact_match;
diff --git a/string-list.h b/string-list.h
index d3809a1417..29bfb7ae45 100644
--- a/string-list.h
+++ b/string-list.h
@@ -63,6 +63,13 @@ int string_list_find_insert_index(const struct string_list *list, const char *st
struct string_list_item *string_list_insert(struct string_list *list, const char *string);
/*
+ * Removes the given string from the sorted list.
+ * If the string doesn't exist, the list is not altered.
+ */
+extern void string_list_remove(struct string_list *list, const char *string,
+ int free_util);
+
+/*
* Checks if the given string is part of a sorted list. If it is part of the list,
* return the coresponding string_list_item, NULL otherwise.
*/
diff --git a/sub-process.c b/sub-process.c
new file mode 100644
index 0000000000..92f8aea70a
--- /dev/null
+++ b/sub-process.c
@@ -0,0 +1,106 @@
+/*
+ * Generic implementation of background process infrastructure.
+ */
+#include "sub-process.h"
+#include "sigchain.h"
+#include "pkt-line.h"
+
+int cmd2process_cmp(const struct subprocess_entry *e1,
+ const struct subprocess_entry *e2,
+ const void *unused)
+{
+ return strcmp(e1->cmd, e2->cmd);
+}
+
+struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd)
+{
+ struct subprocess_entry key;
+
+ hashmap_entry_init(&key, strhash(cmd));
+ key.cmd = cmd;
+ return hashmap_get(hashmap, &key, NULL);
+}
+
+int subprocess_read_status(int fd, struct strbuf *status)
+{
+ struct strbuf **pair;
+ char *line;
+ int len;
+
+ for (;;) {
+ len = packet_read_line_gently(fd, NULL, &line);
+ if ((len < 0) || !line)
+ break;
+ pair = strbuf_split_str(line, '=', 2);
+ if (pair[0] && pair[0]->len && pair[1]) {
+ /* the last "status=<foo>" line wins */
+ if (!strcmp(pair[0]->buf, "status=")) {
+ strbuf_reset(status);
+ strbuf_addbuf(status, pair[1]);
+ }
+ }
+ strbuf_list_free(pair);
+ }
+
+ return (len < 0) ? len : 0;
+}
+
+void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry)
+{
+ if (!entry)
+ return;
+
+ entry->process.clean_on_exit = 0;
+ kill(entry->process.pid, SIGTERM);
+ finish_command(&entry->process);
+
+ hashmap_remove(hashmap, entry, NULL);
+}
+
+static void subprocess_exit_handler(struct child_process *process)
+{
+ sigchain_push(SIGPIPE, SIG_IGN);
+ /* Closing the pipe signals the subprocess to initiate a shutdown. */
+ close(process->in);
+ close(process->out);
+ sigchain_pop(SIGPIPE);
+ /* Finish command will wait until the shutdown is complete. */
+ finish_command(process);
+}
+
+int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
+ subprocess_start_fn startfn)
+{
+ int err;
+ struct child_process *process;
+ const char *argv[] = { cmd, NULL };
+
+ entry->cmd = cmd;
+ process = &entry->process;
+
+ child_process_init(process);
+ process->argv = argv;
+ process->use_shell = 1;
+ process->in = -1;
+ process->out = -1;
+ process->clean_on_exit = 1;
+ process->clean_on_exit_handler = subprocess_exit_handler;
+
+ err = start_command(process);
+ if (err) {
+ error("cannot fork to run subprocess '%s'", cmd);
+ return err;
+ }
+
+ hashmap_entry_init(entry, strhash(cmd));
+
+ err = startfn(entry);
+ if (err) {
+ error("initialization for subprocess '%s' failed", cmd);
+ subprocess_stop(hashmap, entry);
+ return err;
+ }
+
+ hashmap_add(hashmap, entry);
+ return 0;
+}
diff --git a/sub-process.h b/sub-process.h
new file mode 100644
index 0000000000..7d451e1cde
--- /dev/null
+++ b/sub-process.h
@@ -0,0 +1,49 @@
+#ifndef SUBPROCESS_H
+#define SUBPROCESS_H
+
+#include "git-compat-util.h"
+#include "hashmap.h"
+#include "run-command.h"
+
+/*
+ * Generic implementation of background process infrastructure.
+ * See Documentation/technical/api-background-process.txt.
+ */
+
+ /* data structures */
+
+struct subprocess_entry {
+ struct hashmap_entry ent; /* must be the first member! */
+ const char *cmd;
+ struct child_process process;
+};
+
+/* subprocess functions */
+
+int cmd2process_cmp(const struct subprocess_entry *e1,
+ const struct subprocess_entry *e2, const void *unused);
+
+typedef int(*subprocess_start_fn)(struct subprocess_entry *entry);
+int subprocess_start(struct hashmap *hashmap, struct subprocess_entry *entry, const char *cmd,
+ subprocess_start_fn startfn);
+
+void subprocess_stop(struct hashmap *hashmap, struct subprocess_entry *entry);
+
+struct subprocess_entry *subprocess_find_entry(struct hashmap *hashmap, const char *cmd);
+
+/* subprocess helper functions */
+
+static inline struct child_process *subprocess_get_child_process(
+ struct subprocess_entry *entry)
+{
+ return &entry->process;
+}
+
+/*
+ * Helper function that will read packets looking for "status=<foo>"
+ * key/value pairs and return the value from the last "status" packet
+ */
+
+int subprocess_read_status(int fd, struct strbuf *status);
+
+#endif
diff --git a/submodule.c b/submodule.c
index d3299e29c0..bf5a93d16f 100644
--- a/submodule.c
+++ b/submodule.c
@@ -20,7 +20,7 @@
static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
static int parallel_jobs = 1;
-static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP;
+static struct string_list changed_submodule_paths = STRING_LIST_INIT_DUP;
static int initialized_fetch_ref_tips;
static struct oid_array ref_tips_before_fetch;
static struct oid_array ref_tips_after_fetch;
@@ -282,6 +282,69 @@ int is_submodule_populated_gently(const char *path, int *return_error_code)
return ret;
}
+/*
+ * Dies if the provided 'prefix' corresponds to an unpopulated submodule
+ */
+void die_in_unpopulated_submodule(const struct index_state *istate,
+ const char *prefix)
+{
+ int i, prefixlen;
+
+ if (!prefix)
+ return;
+
+ prefixlen = strlen(prefix);
+
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
+ int ce_len = ce_namelen(ce);
+
+ if (!S_ISGITLINK(ce->ce_mode))
+ continue;
+ if (prefixlen <= ce_len)
+ continue;
+ if (strncmp(ce->name, prefix, ce_len))
+ continue;
+ if (prefix[ce_len] != '/')
+ continue;
+
+ die(_("in unpopulated submodule '%s'"), ce->name);
+ }
+}
+
+/*
+ * Dies if any paths in the provided pathspec descends into a submodule
+ */
+void die_path_inside_submodule(const struct index_state *istate,
+ const struct pathspec *ps)
+{
+ int i, j;
+
+ for (i = 0; i < istate->cache_nr; i++) {
+ struct cache_entry *ce = istate->cache[i];
+ int ce_len = ce_namelen(ce);
+
+ if (!S_ISGITLINK(ce->ce_mode))
+ continue;
+
+ for (j = 0; j < ps->nr ; j++) {
+ const struct pathspec_item *item = &ps->items[j];
+
+ if (item->len <= ce_len)
+ continue;
+ if (item->match[ce_len] != '/')
+ continue;
+ if (strncmp(ce->name, item->match, ce_len))
+ continue;
+ if (item->len == ce_len + 1)
+ continue;
+
+ die(_("Pathspec '%s' is in submodule '%.*s'"),
+ item->original, ce_len, ce->name);
+ }
+ }
+}
+
int parse_submodule_update_strategy(const char *value,
struct submodule_update_strategy *dst)
{
@@ -447,8 +510,8 @@ static void show_submodule_header(FILE *f, const char *path,
* Attempt to lookup the commit references, and determine if this is
* a fast forward or fast backwards update.
*/
- *left = lookup_commit_reference(one->hash);
- *right = lookup_commit_reference(two->hash);
+ *left = lookup_commit_reference(one);
+ *right = lookup_commit_reference(two);
/*
* Warn about missing commits in the submodule project, but only if
@@ -554,7 +617,8 @@ void show_submodule_inline_diff(FILE *f, const char *path,
cp.no_stdin = 1;
/* TODO: other options may need to be passed here. */
- argv_array_push(&cp.args, "diff");
+ argv_array_pushl(&cp.args, "diff", "--submodule=diff", NULL);
+
argv_array_pushf(&cp.args, "--line-prefix=%s", line_prefix);
if (DIFF_OPT_TST(o, REVERSE_DIFF)) {
argv_array_pushf(&cp.args, "--src-prefix=%s%s/",
@@ -617,6 +681,94 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)
return submodule_from_path(null_sha1, ce->name);
}
+static struct oid_array *submodule_commits(struct string_list *submodules,
+ const char *path)
+{
+ struct string_list_item *item;
+
+ item = string_list_insert(submodules, path);
+ if (item->util)
+ return (struct oid_array *) item->util;
+
+ /* NEEDSWORK: should we have oid_array_init()? */
+ item->util = xcalloc(1, sizeof(struct oid_array));
+ return (struct oid_array *) item->util;
+}
+
+static void collect_changed_submodules_cb(struct diff_queue_struct *q,
+ struct diff_options *options,
+ void *data)
+{
+ int i;
+ struct string_list *changed = data;
+
+ for (i = 0; i < q->nr; i++) {
+ struct diff_filepair *p = q->queue[i];
+ struct oid_array *commits;
+ if (!S_ISGITLINK(p->two->mode))
+ continue;
+
+ if (S_ISGITLINK(p->one->mode)) {
+ /*
+ * NEEDSWORK: We should honor the name configured in
+ * the .gitmodules file of the commit we are examining
+ * here to be able to correctly follow submodules
+ * being moved around.
+ */
+ commits = submodule_commits(changed, p->two->path);
+ oid_array_append(commits, &p->two->oid);
+ } else {
+ /* Submodule is new or was moved here */
+ /*
+ * NEEDSWORK: When the .git directories of submodules
+ * live inside the superprojects .git directory some
+ * day we should fetch new submodules directly into
+ * that location too when config or options request
+ * that so they can be checked out from there.
+ */
+ continue;
+ }
+ }
+}
+
+/*
+ * Collect the paths of submodules in 'changed' which have changed based on
+ * the revisions as specified in 'argv'. Each entry in 'changed' will also
+ * 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,
+ struct argv_array *argv)
+{
+ struct rev_info rev;
+ const struct commit *commit;
+
+ init_revisions(&rev, NULL);
+ setup_revisions(argv->argc, argv->argv, &rev, NULL);
+ if (prepare_revision_walk(&rev))
+ die("revision walk setup failed");
+
+ while ((commit = get_revision(&rev))) {
+ struct rev_info diff_rev;
+
+ init_revisions(&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 = changed;
+ diff_tree_combined_merge(commit, 1, &diff_rev);
+ }
+
+ reset_revision_walk();
+}
+
+static void free_submodules_oids(struct string_list *submodules)
+{
+ struct string_list_item *item;
+ for_each_string_list_item(item, submodules)
+ oid_array_clear((struct oid_array *) item->util);
+ string_list_clear(submodules, 1);
+}
+
static int has_remote(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
@@ -634,7 +786,7 @@ static int check_has_commit(const struct object_id *oid, void *data)
{
int *has_commit = data;
- if (!lookup_commit_reference(oid->hash))
+ if (!lookup_commit_reference(oid))
*has_commit = 0;
return 0;
@@ -644,10 +796,44 @@ static int submodule_has_commits(const char *path, struct oid_array *commits)
{
int has_commit = 1;
+ /*
+ * Perform a cheap, but incorrect check for the existance of 'commits'.
+ * This is done by adding the submodule's object store to the in-core
+ * object store, and then querying for each commit's existance. If we
+ * do not have the commit object anywhere, there is no chance we have
+ * it in the object store of the correct submodule and have it
+ * reachable from a ref, so we can fail early without spawning rev-list
+ * which is expensive.
+ */
if (add_submodule_odb(path))
return 0;
oid_array_for_each_unique(commits, check_has_commit, &has_commit);
+
+ if (has_commit) {
+ /*
+ * Even if the submodule is checked out and the commit is
+ * present, make sure it exists in the submodule's object store
+ * and that it is reachable from a ref.
+ */
+ struct child_process cp = CHILD_PROCESS_INIT;
+ struct strbuf out = STRBUF_INIT;
+
+ argv_array_pushl(&cp.args, "rev-list", "-n", "1", NULL);
+ oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
+ argv_array_pushl(&cp.args, "--not", "--all", NULL);
+
+ prepare_submodule_repo_env(&cp.env_array);
+ cp.git_cmd = 1;
+ cp.no_stdin = 1;
+ cp.dir = path;
+
+ if (capture_command(&cp, &out, GIT_MAX_HEXSZ + 1) || out.len)
+ has_commit = 0;
+
+ strbuf_release(&out);
+ }
+
return has_commit;
}
@@ -695,91 +881,31 @@ static int submodule_needs_pushing(const char *path, struct oid_array *commits)
return 0;
}
-static struct oid_array *submodule_commits(struct string_list *submodules,
- const char *path)
-{
- struct string_list_item *item;
-
- item = string_list_insert(submodules, path);
- if (item->util)
- return (struct oid_array *) item->util;
-
- /* NEEDSWORK: should we have oid_array_init()? */
- item->util = xcalloc(1, sizeof(struct oid_array));
- return (struct oid_array *) item->util;
-}
-
-static void collect_submodules_from_diff(struct diff_queue_struct *q,
- struct diff_options *options,
- void *data)
-{
- int i;
- struct string_list *submodules = data;
-
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- struct oid_array *commits;
- if (!S_ISGITLINK(p->two->mode))
- continue;
- commits = submodule_commits(submodules, p->two->path);
- oid_array_append(commits, &p->two->oid);
- }
-}
-
-static void find_unpushed_submodule_commits(struct commit *commit,
- struct string_list *needs_pushing)
-{
- struct rev_info rev;
-
- init_revisions(&rev, NULL);
- rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK;
- rev.diffopt.format_callback = collect_submodules_from_diff;
- rev.diffopt.format_callback_data = needs_pushing;
- diff_tree_combined_merge(commit, 1, &rev);
-}
-
-static void free_submodules_sha1s(struct string_list *submodules)
-{
- struct string_list_item *item;
- for_each_string_list_item(item, submodules)
- oid_array_clear((struct oid_array *) item->util);
- string_list_clear(submodules, 1);
-}
-
int find_unpushed_submodules(struct oid_array *commits,
const char *remotes_name, struct string_list *needs_pushing)
{
- struct rev_info rev;
- struct commit *commit;
struct string_list submodules = STRING_LIST_INIT_DUP;
struct string_list_item *submodule;
struct argv_array argv = ARGV_ARRAY_INIT;
- init_revisions(&rev, NULL);
-
/* argv.argv[0] will be ignored by setup_revisions */
argv_array_push(&argv, "find_unpushed_submodules");
oid_array_for_each_unique(commits, append_oid_to_argv, &argv);
argv_array_push(&argv, "--not");
argv_array_pushf(&argv, "--remotes=%s", remotes_name);
- setup_revisions(argv.argc, argv.argv, &rev, NULL);
- if (prepare_revision_walk(&rev))
- die("revision walk setup failed");
-
- while ((commit = get_revision(&rev)) != NULL)
- find_unpushed_submodule_commits(commit, &submodules);
-
- reset_revision_walk();
- argv_array_clear(&argv);
+ collect_changed_submodules(&submodules, &argv);
for_each_string_list_item(submodule, &submodules) {
- struct oid_array *commits = (struct oid_array *) submodule->util;
+ struct oid_array *commits = submodule->util;
+ const char *path = submodule->string;
- if (submodule_needs_pushing(submodule->string, commits))
- string_list_insert(needs_pushing, submodule->string);
+ if (submodule_needs_pushing(path, commits))
+ string_list_insert(needs_pushing, path);
}
- free_submodules_sha1s(&submodules);
+
+ free_submodules_oids(&submodules);
+ argv_array_clear(&argv);
return needs_pushing->nr;
}
@@ -896,125 +1022,56 @@ int push_unpushed_submodules(struct oid_array *commits,
return ret;
}
-static int is_submodule_commit_present(const char *path, unsigned char sha1[20])
-{
- int is_present = 0;
- if (!add_submodule_odb(path) && lookup_commit_reference(sha1)) {
- /* Even if the submodule is checked out and the commit is
- * present, make sure it is reachable from a ref. */
- struct child_process cp = CHILD_PROCESS_INIT;
- const char *argv[] = {"rev-list", "-n", "1", NULL, "--not", "--all", NULL};
- struct strbuf buf = STRBUF_INIT;
-
- argv[3] = sha1_to_hex(sha1);
- cp.argv = argv;
- prepare_submodule_repo_env(&cp.env_array);
- cp.git_cmd = 1;
- cp.no_stdin = 1;
- cp.dir = path;
- if (!capture_command(&cp, &buf, 1024) && !buf.len)
- is_present = 1;
-
- strbuf_release(&buf);
- }
- return is_present;
-}
-
-static void submodule_collect_changed_cb(struct diff_queue_struct *q,
- struct diff_options *options,
- void *data)
+static int append_oid_to_array(const char *ref, const struct object_id *oid,
+ int flags, void *data)
{
- int i;
- for (i = 0; i < q->nr; i++) {
- struct diff_filepair *p = q->queue[i];
- if (!S_ISGITLINK(p->two->mode))
- continue;
-
- if (S_ISGITLINK(p->one->mode)) {
- /* NEEDSWORK: We should honor the name configured in
- * the .gitmodules file of the commit we are examining
- * here to be able to correctly follow submodules
- * being moved around. */
- struct string_list_item *path;
- path = unsorted_string_list_lookup(&changed_submodule_paths, p->two->path);
- if (!path && !is_submodule_commit_present(p->two->path, p->two->oid.hash))
- string_list_append(&changed_submodule_paths, xstrdup(p->two->path));
- } else {
- /* Submodule is new or was moved here */
- /* NEEDSWORK: When the .git directories of submodules
- * live inside the superprojects .git directory some
- * day we should fetch new submodules directly into
- * that location too when config or options request
- * that so they can be checked out from there. */
- continue;
- }
- }
-}
-
-static int add_sha1_to_array(const char *ref, const struct object_id *oid,
- int flags, void *data)
-{
- oid_array_append(data, oid);
+ struct oid_array *array = data;
+ oid_array_append(array, oid);
return 0;
}
void check_for_new_submodule_commits(struct object_id *oid)
{
if (!initialized_fetch_ref_tips) {
- for_each_ref(add_sha1_to_array, &ref_tips_before_fetch);
+ for_each_ref(append_oid_to_array, &ref_tips_before_fetch);
initialized_fetch_ref_tips = 1;
}
oid_array_append(&ref_tips_after_fetch, oid);
}
-static int add_oid_to_argv(const struct object_id *oid, void *data)
-{
- argv_array_push(data, oid_to_hex(oid));
- return 0;
-}
-
static void calculate_changed_submodule_paths(void)
{
- struct rev_info rev;
- struct commit *commit;
struct argv_array argv = ARGV_ARRAY_INIT;
+ struct string_list changed_submodules = STRING_LIST_INIT_DUP;
+ const struct string_list_item *item;
/* No need to check if there are no submodules configured */
if (!submodule_from_path(NULL, NULL))
return;
- init_revisions(&rev, NULL);
argv_array_push(&argv, "--"); /* argv[0] program name */
oid_array_for_each_unique(&ref_tips_after_fetch,
- add_oid_to_argv, &argv);
+ append_oid_to_argv, &argv);
argv_array_push(&argv, "--not");
oid_array_for_each_unique(&ref_tips_before_fetch,
- add_oid_to_argv, &argv);
- setup_revisions(argv.argc, argv.argv, &rev, NULL);
- if (prepare_revision_walk(&rev))
- die("revision walk setup failed");
+ append_oid_to_argv, &argv);
/*
* Collect all submodules (whether checked out or not) for which new
* commits have been recorded upstream in "changed_submodule_paths".
*/
- while ((commit = get_revision(&rev))) {
- struct commit_list *parent = commit->parents;
- while (parent) {
- struct diff_options diff_opts;
- diff_setup(&diff_opts);
- DIFF_OPT_SET(&diff_opts, RECURSIVE);
- diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
- diff_opts.format_callback = submodule_collect_changed_cb;
- diff_setup_done(&diff_opts);
- diff_tree_sha1(parent->item->object.oid.hash, commit->object.oid.hash, "", &diff_opts);
- diffcore_std(&diff_opts);
- diff_flush(&diff_opts);
- parent = parent->next;
- }
+ collect_changed_submodules(&changed_submodules, &argv);
+
+ for_each_string_list_item(item, &changed_submodules) {
+ struct oid_array *commits = item->util;
+ const char *path = item->string;
+
+ if (!submodule_has_commits(path, commits))
+ string_list_append(&changed_submodule_paths, path);
}
+ free_submodules_oids(&changed_submodules);
argv_array_clear(&argv);
oid_array_clear(&ref_tips_before_fetch);
oid_array_clear(&ref_tips_after_fetch);
@@ -1363,7 +1420,7 @@ static int submodule_has_dirty_index(const struct submodule *sub)
{
struct child_process cp = CHILD_PROCESS_INIT;
- prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
argv_array_pushl(&cp.args, "diff-index", "--quiet",
@@ -1380,7 +1437,7 @@ static int submodule_has_dirty_index(const struct submodule *sub)
static void submodule_reset_index(const char *path)
{
struct child_process cp = CHILD_PROCESS_INIT;
- prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
@@ -1409,6 +1466,23 @@ int submodule_move_head(const char *path,
int ret = 0;
struct child_process cp = CHILD_PROCESS_INIT;
const struct submodule *sub;
+ int *error_code_ptr, error_code;
+
+ if (!is_submodule_initialized(path))
+ return 0;
+
+ if (flags & SUBMODULE_MOVE_HEAD_FORCE)
+ /*
+ * Pass non NULL pointer to is_submodule_populated_gently
+ * to prevent die()-ing. We'll use connect_work_tree_and_git_dir
+ * to fixup the submodule in the force case later.
+ */
+ error_code_ptr = &error_code;
+ else
+ error_code_ptr = NULL;
+
+ if (old && !is_submodule_populated_gently(path, error_code_ptr))
+ return 0;
sub = submodule_from_path(null_sha1, path);
@@ -1427,18 +1501,24 @@ int submodule_move_head(const char *path,
absorb_git_dir_into_superproject("", path,
ABSORB_GITDIR_RECURSE_SUBMODULES);
} else {
- struct strbuf sb = STRBUF_INIT;
- strbuf_addf(&sb, "%s/modules/%s",
+ char *gitdir = xstrfmt("%s/modules/%s",
get_git_common_dir(), sub->name);
- connect_work_tree_and_git_dir(path, sb.buf);
- strbuf_release(&sb);
+ connect_work_tree_and_git_dir(path, gitdir);
+ free(gitdir);
/* make sure the index is clean as well */
submodule_reset_index(path);
}
+
+ if (old && (flags & SUBMODULE_MOVE_HEAD_FORCE)) {
+ char *gitdir = xstrfmt("%s/modules/%s",
+ get_git_common_dir(), sub->name);
+ connect_work_tree_and_git_dir(path, gitdir);
+ free(gitdir);
+ }
}
- prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+ prepare_submodule_repo_env(&cp.env_array);
cp.git_cmd = 1;
cp.no_stdin = 1;
@@ -1446,7 +1526,7 @@ int submodule_move_head(const char *path,
argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
get_super_prefix_or_empty(), path);
- argv_array_pushl(&cp.args, "read-tree", NULL);
+ argv_array_pushl(&cp.args, "read-tree", "--recurse-submodules", NULL);
if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
argv_array_push(&cp.args, "-n");
@@ -1468,15 +1548,16 @@ int submodule_move_head(const char *path,
if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
if (new) {
- struct child_process cp1 = CHILD_PROCESS_INIT;
+ child_process_init(&cp);
/* also set the HEAD accordingly */
- cp1.git_cmd = 1;
- cp1.no_stdin = 1;
- cp1.dir = path;
+ cp.git_cmd = 1;
+ cp.no_stdin = 1;
+ cp.dir = path;
- argv_array_pushl(&cp1.args, "update-ref", "HEAD", new, NULL);
+ prepare_submodule_repo_env(&cp.env_array);
+ argv_array_pushl(&cp.args, "update-ref", "HEAD", new, NULL);
- if (run_command(&cp1)) {
+ if (run_command(&cp)) {
ret = -1;
goto out;
}
@@ -1566,9 +1647,9 @@ static void print_commit(struct commit *commit)
#define MERGE_WARNING(path, msg) \
warning("Failed to merge submodule %s (%s)", path, msg);
-int merge_submodule(unsigned char result[20], const char *path,
- const unsigned char base[20], const unsigned char a[20],
- const unsigned char b[20], int search)
+int merge_submodule(struct object_id *result, const char *path,
+ const struct object_id *base, const struct object_id *a,
+ const struct object_id *b, int search)
{
struct commit *commit_base, *commit_a, *commit_b;
int parent_count;
@@ -1577,14 +1658,14 @@ int merge_submodule(unsigned char result[20], const char *path,
int i;
/* store a in result in case we fail */
- hashcpy(result, a);
+ oidcpy(result, a);
/* we can not handle deletion conflicts */
- if (is_null_sha1(base))
+ if (is_null_oid(base))
return 0;
- if (is_null_sha1(a))
+ if (is_null_oid(a))
return 0;
- if (is_null_sha1(b))
+ if (is_null_oid(b))
return 0;
if (add_submodule_odb(path)) {
@@ -1608,11 +1689,11 @@ int merge_submodule(unsigned char result[20], const char *path,
/* Case #1: a is contained in b or vice versa */
if (in_merge_bases(commit_a, commit_b)) {
- hashcpy(result, b);
+ oidcpy(result, b);
return 1;
}
if (in_merge_bases(commit_b, commit_a)) {
- hashcpy(result, a);
+ oidcpy(result, a);
return 1;
}
diff --git a/submodule.h b/submodule.h
index 1277480add..8fb0f25498 100644
--- a/submodule.h
+++ b/submodule.h
@@ -49,6 +49,10 @@ extern int is_submodule_initialized(const char *path);
* Otherwise the return error code is the same as of resolve_gitdir_gently.
*/
extern int is_submodule_populated_gently(const char *path, int *return_error_code);
+extern void die_in_unpopulated_submodule(const struct index_state *istate,
+ const char *prefix);
+extern void die_path_inside_submodule(const struct index_state *istate,
+ const struct pathspec *ps);
extern int parse_submodule_update_strategy(const char *value,
struct submodule_update_strategy *dst);
extern const char *submodule_strategy_to_string(const struct submodule_update_strategy *s);
@@ -84,10 +88,10 @@ extern int submodule_uses_gitfile(const char *path);
#define SUBMODULE_REMOVAL_IGNORE_UNTRACKED (1<<1)
#define SUBMODULE_REMOVAL_IGNORE_IGNORED_UNTRACKED (1<<2)
extern int bad_to_remove_submodule(const char *path, unsigned flags);
-extern int merge_submodule(unsigned char result[20], const char *path,
- const unsigned char base[20],
- const unsigned char a[20],
- const unsigned char b[20], int search);
+extern int merge_submodule(struct object_id *result, const char *path,
+ const struct object_id *base,
+ const struct object_id *a,
+ const struct object_id *b, int search);
extern int find_unpushed_submodules(struct oid_array *commits,
const char *remotes_name,
struct string_list *needs_pushing);
diff --git a/t/README b/t/README
index ab386c3681..2f95860369 100644
--- a/t/README
+++ b/t/README
@@ -803,9 +803,9 @@ use these, and "test_set_prereq" for how to define your own.
Test is not run by root user, and an attempt to write to an
unwritable file is expected to fail correctly.
- - LIBPCRE
+ - PCRE
- Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests
+ Git was compiled with support for PCRE. Wrap any tests
that use git-grep --perl-regexp or git-grep -P in these.
- CASE_INSENSITIVE_FS
@@ -817,6 +817,10 @@ use these, and "test_set_prereq" for how to define your own.
Test is run on a filesystem which converts decomposed utf-8 (nfd)
to precomposed utf-8 (nfc).
+ - PTHREADS
+
+ Git wasn't compiled with NO_PTHREADS=YesPlease.
+
Tips for Writing Tests
----------------------
diff --git a/t/helper/test-date.c b/t/helper/test-date.c
index 506054bcd5..f414a3ac67 100644
--- a/t/helper/test-date.c
+++ b/t/helper/test-date.c
@@ -4,7 +4,9 @@ static const char *usage_msg = "\n"
" test-date relative [time_t]...\n"
" test-date show:<format> [time_t]...\n"
" test-date parse [date]...\n"
-" test-date approxidate [date]...\n";
+" test-date approxidate [date]...\n"
+" test-date is64bit\n"
+" test-date time_t-is64bit\n";
static void show_relative_dates(const char **argv, struct timeval *now)
{
@@ -25,14 +27,14 @@ static void show_dates(const char **argv, const char *format)
parse_date_format(format, &mode);
for (; *argv; argv++) {
char *arg;
- time_t t;
+ timestamp_t t;
int tz;
/*
* Do not use our normal timestamp parsing here, as the point
* is to test the formatting code in isolation.
*/
- t = strtol(*argv, &arg, 10);
+ t = parse_timestamp(*argv, &arg, 10);
while (*arg == ' ')
arg++;
tz = atoi(arg);
@@ -46,12 +48,12 @@ static void parse_dates(const char **argv, struct timeval *now)
struct strbuf result = STRBUF_INIT;
for (; *argv; argv++) {
- unsigned long t;
+ timestamp_t t;
int tz;
strbuf_reset(&result);
parse_date(*argv, &result);
- if (sscanf(result.buf, "%lu %d", &t, &tz) == 2)
+ if (sscanf(result.buf, "%"PRItime" %d", &t, &tz) == 2)
printf("%s -> %s\n",
*argv, show_date(t, tz, DATE_MODE(ISO8601)));
else
@@ -63,7 +65,7 @@ static void parse_dates(const char **argv, struct timeval *now)
static void parse_approxidate(const char **argv, struct timeval *now)
{
for (; *argv; argv++) {
- time_t t;
+ timestamp_t t;
t = approxidate_relative(*argv, now);
printf("%s -> %s\n", *argv, show_date(t, 0, DATE_MODE(ISO8601)));
}
@@ -93,6 +95,10 @@ int cmd_main(int argc, const char **argv)
parse_dates(argv+1, &now);
else if (!strcmp(*argv, "approxidate"))
parse_approxidate(argv+1, &now);
+ else if (!strcmp(*argv, "is64bit"))
+ return sizeof(timestamp_t) == 8 ? 0 : 1;
+ else if (!strcmp(*argv, "time_t-is64bit"))
+ return sizeof(time_t) == 8 ? 0 : 1;
else
usage(usage_msg);
return 0;
diff --git a/t/helper/test-dump-cache-tree.c b/t/helper/test-dump-cache-tree.c
index 7af116d49e..ebf3aab22d 100644
--- a/t/helper/test-dump-cache-tree.c
+++ b/t/helper/test-dump-cache-tree.c
@@ -10,7 +10,7 @@ static void dump_one(struct cache_tree *it, const char *pfx, const char *x)
"invalid", x, pfx, it->subtree_nr);
else
printf("%s %s%s (%d entries, %d subtrees)\n",
- sha1_to_hex(it->sha1), x, pfx,
+ oid_to_hex(&it->oid), x, pfx,
it->entry_count, it->subtree_nr);
}
@@ -32,7 +32,7 @@ static int dump_cache_tree(struct cache_tree *it,
}
else {
dump_one(it, pfx, "");
- if (hashcmp(it->sha1, ref->sha1) ||
+ if (oidcmp(&it->oid, &ref->oid) ||
ref->entry_count != it->entry_count ||
ref->subtree_nr != it->subtree_nr) {
/* claims to be valid but is lying */
diff --git a/t/helper/test-match-trees.c b/t/helper/test-match-trees.c
index e939502863..356d8edef1 100644
--- a/t/helper/test-match-trees.c
+++ b/t/helper/test-match-trees.c
@@ -12,10 +12,10 @@ int cmd_main(int ac, const char **av)
die("cannot parse %s as an object name", av[1]);
if (get_oid(av[2], &hash2))
die("cannot parse %s as an object name", av[2]);
- one = parse_tree_indirect(hash1.hash);
+ one = parse_tree_indirect(&hash1);
if (!one)
die("not a tree-ish %s", av[1]);
- two = parse_tree_indirect(hash2.hash);
+ two = parse_tree_indirect(&hash2);
if (!two)
die("not a tree-ish %s", av[2]);
diff --git a/t/helper/test-parse-options.c b/t/helper/test-parse-options.c
index a01430c24b..75fe883aac 100644
--- a/t/helper/test-parse-options.c
+++ b/t/helper/test-parse-options.c
@@ -5,7 +5,7 @@
static int boolean = 0;
static int integer = 0;
static unsigned long magnitude = 0;
-static unsigned long timestamp;
+static timestamp_t timestamp;
static int abbrev = 7;
static int verbose = -1; /* unspecified */
static int dry_run = 0, quiet = 0;
@@ -161,7 +161,7 @@ int cmd_main(int argc, const char **argv)
show(&expect, &ret, "boolean: %d", boolean);
show(&expect, &ret, "integer: %d", integer);
show(&expect, &ret, "magnitude: %lu", magnitude);
- show(&expect, &ret, "timestamp: %lu", timestamp);
+ show(&expect, &ret, "timestamp: %"PRItime, timestamp);
show(&expect, &ret, "string: %s", string ? string : "(not set)");
show(&expect, &ret, "abbrev: %d", abbrev);
show(&expect, &ret, "verbose: %d", verbose);
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
index 2d84c45ffe..05d8c4d8af 100644
--- a/t/helper/test-ref-store.c
+++ b/t/helper/test-ref-store.c
@@ -1,5 +1,6 @@
#include "cache.h"
#include "refs.h"
+#include "worktree.h"
static const char *notnull(const char *arg, const char *name)
{
@@ -32,6 +33,23 @@ static const char **get_store(const char **argv, struct ref_store **refs)
strbuf_release(&sb);
*refs = get_submodule_ref_store(gitdir);
+ } else if (skip_prefix(argv[0], "worktree:", &gitdir)) {
+ struct worktree **p, **worktrees = get_worktrees(0);
+
+ for (p = worktrees; *p; p++) {
+ struct worktree *wt = *p;
+
+ if (!wt->id) {
+ /* special case for main worktree */
+ if (!strcmp(gitdir, "main"))
+ break;
+ } else if (!strcmp(gitdir, wt->id))
+ break;
+ }
+ if (!*p)
+ die("no such worktree: %s", gitdir);
+
+ *refs = get_worktree_ref_store(*p);
} else
die("unknown backend %s", argv[0]);
@@ -75,12 +93,13 @@ static int cmd_create_symref(struct ref_store *refs, const char **argv)
static int cmd_delete_refs(struct ref_store *refs, const char **argv)
{
unsigned int flags = arg_flags(*argv++, "flags");
+ const char *msg = *argv++;
struct string_list refnames = STRING_LIST_INIT_NODUP;
while (*argv)
string_list_append(&refnames, *argv++);
- return refs_delete_refs(refs, &refnames, flags);
+ return refs_delete_refs(refs, msg, &refnames, flags);
}
static int cmd_rename_ref(struct ref_store *refs, const char **argv)
@@ -138,10 +157,10 @@ static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
}
static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
- const char *committer, unsigned long timestamp,
+ const char *committer, timestamp_t timestamp,
int tz, const char *msg, void *cb_data)
{
- printf("%s %s %s %lu %d %s\n",
+ printf("%s %s %s %"PRItime" %d %s\n",
oid_to_hex(old_oid), oid_to_hex(new_oid),
committer, timestamp, tz, msg);
return 0;
diff --git a/t/lib-submodule-update.sh b/t/lib-submodule-update.sh
index fb4f7b014e..58bd4aeb2c 100755
--- a/t/lib-submodule-update.sh
+++ b/t/lib-submodule-update.sh
@@ -73,6 +73,7 @@ create_lib_submodule_repo () {
git checkout -b "add_sub1" &&
git submodule add ../submodule_update_sub1 sub1 &&
+ git submodule add ../submodule_update_sub1 uninitialized_sub &&
git config -f .gitmodules submodule.sub1.ignore all &&
git config submodule.sub1.ignore all &&
git add .gitmodules &&
@@ -787,11 +788,6 @@ test_submodule_switch_recursing () {
then
RESULTDS=failure
fi
- RESULTR=success
- if test "$KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED" = 1
- then
- RESULTR=failure
- fi
RESULTOI=success
if test "$KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED" = 1
then
@@ -1003,7 +999,7 @@ test_submodule_switch_recursing () {
'
# recursing deeper than one level doesn't work yet.
- test_expect_$RESULTR "$command: modified submodule updates submodule recursively" '
+ test_expect_success "$command: modified submodule updates submodule recursively" '
prolog &&
reset_work_tree_to_interested add_nested_sub &&
(
@@ -1212,14 +1208,31 @@ test_submodule_forced_switch_recursing () {
)
'
# Updating a submodule from an invalid sha1 updates
- test_expect_success "$command: modified submodule does not update submodule work tree from invalid commit" '
+ test_expect_success "$command: modified submodule does update submodule work tree from invalid commit" '
prolog &&
reset_work_tree_to_interested invalid_sub1 &&
(
cd submodule_update &&
git branch -t valid_sub1 origin/valid_sub1 &&
- test_must_fail $command valid_sub1 &&
- test_superproject_content origin/invalid_sub1
+ $command valid_sub1 &&
+ test_superproject_content origin/valid_sub1 &&
+ test_submodule_content sub1 origin/valid_sub1
+ )
+ '
+
+ # Old versions of Git were buggy writing the .git link file
+ # (e.g. before f8eaa0ba98b and then moving the superproject repo
+ # whose submodules contained absolute paths)
+ test_expect_success "$command: updating submodules fixes .git links" '
+ prolog &&
+ reset_work_tree_to_interested add_sub1 &&
+ (
+ cd submodule_update &&
+ git branch -t modify_sub1 origin/modify_sub1 &&
+ echo "gitdir: bogus/path" >sub1/.git &&
+ $command modify_sub1 &&
+ test_superproject_content origin/modify_sub1 &&
+ test_submodule_content sub1 origin/modify_sub1
)
'
}
diff --git a/t/perf/README b/t/perf/README
index 49ea4349be..21321a0f36 100644
--- a/t/perf/README
+++ b/t/perf/README
@@ -60,7 +60,22 @@ You can set the following variables (also in your config.mak):
GIT_PERF_MAKE_OPTS
Options to use when automatically building a git tree for
- performance testing. E.g., -j6 would be useful.
+ performance testing. E.g., -j6 would be useful. Passed
+ directly to make as "make $GIT_PERF_MAKE_OPTS".
+
+ GIT_PERF_MAKE_COMMAND
+ An arbitrary command that'll be run in place of the make
+ command, if set the GIT_PERF_MAKE_OPTS variable is
+ ignored. Useful in cases where source tree changes might
+ require issuing a different make command to different
+ revisions.
+
+ This can be (ab)used to monkeypatch or otherwise change the
+ tree about to be built. Note that the build directory can be
+ re-used for subsequent runs so the make command might get
+ executed multiple times on the same tree, but don't count on
+ any of that, that's an implementation detail that might change
+ in the future.
GIT_PERF_REPO
GIT_PERF_LARGE_REPO
@@ -106,6 +121,7 @@ sources perf-lib.sh:
After that you will want to use some of the following:
+ test_perf_fresh_repo # sets up an empty repository
test_perf_default_repo # sets up a "normal" repository
test_perf_large_repo # sets up a "large" repository
diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh
index 5afa8c8df3..8de5a98cfc 100755
--- a/t/perf/p0004-lazy-init-name-hash.sh
+++ b/t/perf/p0004-lazy-init-name-hash.sh
@@ -7,13 +7,50 @@ test_perf_large_repo
test_checkout_worktree
test_expect_success 'verify both methods build the same hashmaps' '
- $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --dump --single | sort >out.single &&
- $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --dump --multi | sort >out.multi &&
- test_cmp out.single out.multi
+ test-lazy-init-name-hash --dump --single >out.single &&
+ if test-lazy-init-name-hash --dump --multi >out.multi
+ then
+ test_set_prereq REPO_BIG_ENOUGH_FOR_MULTI &&
+ sort <out.single >sorted.single &&
+ sort <out.multi >sorted.multi &&
+ test_cmp sorted.single sorted.multi
+ fi
'
-test_expect_success 'multithreaded should be faster' '
- $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --perf >out.perf
+test_expect_success 'calibrate' '
+ entries=$(wc -l <out.single) &&
+
+ case $entries in
+ ?) count=1000000 ;;
+ ??) count=100000 ;;
+ ???) count=10000 ;;
+ ????) count=1000 ;;
+ ?????) count=100 ;;
+ ??????) count=10 ;;
+ *) count=1 ;;
+ esac &&
+ export count &&
+
+ case $entries in
+ 1) entries_desc="1 entry" ;;
+ *) entries_desc="$entries entries" ;;
+ esac &&
+
+ case $count in
+ 1) count_desc="1 round" ;;
+ *) count_desc="$count rounds" ;;
+ esac &&
+
+ desc="$entries_desc, $count_desc" &&
+ export desc
'
+test_perf "single-threaded, $desc" "
+ test-lazy-init-name-hash --single --count=$count
+"
+
+test_perf REPO_BIG_ENOUGH_FOR_MULTI "multi-threaded, $desc" "
+ test-lazy-init-name-hash --multi --count=$count
+"
+
test_done
diff --git a/t/perf/p0100-globbing.sh b/t/perf/p0100-globbing.sh
new file mode 100755
index 0000000000..dd18a9ce2b
--- /dev/null
+++ b/t/perf/p0100-globbing.sh
@@ -0,0 +1,43 @@
+#!/bin/sh
+
+test_description="Tests pathological globbing performance
+
+Shows how Git's globbing performance performs when given the sort of
+pathological patterns described in at https://research.swtch.com/glob
+"
+
+. ./perf-lib.sh
+
+test_globs_big='10 25 50 75 100'
+test_globs_small='1 2 3 4 5 6'
+
+test_perf_fresh_repo
+
+test_expect_success 'setup' '
+ for i in $(test_seq 1 100)
+ do
+ printf "a" >>refname &&
+ for j in $(test_seq 1 $i)
+ do
+ printf "a*" >>refglob.$i
+ done &&
+ echo b >>refglob.$i
+ done &&
+ test_commit test $(cat refname).t "" $(cat refname).t
+'
+
+for i in $test_globs_small
+do
+ test_perf "refglob((a*)^nb) against tag (a^100).t; n = $i" '
+ git for-each-ref "refs/tags/$(cat refglob.'$i')b"
+ '
+done
+
+for i in $test_globs_small
+do
+ test_perf "fileglob((a*)^nb) against file (a^100).t; n = $i" '
+ git ls-files "$(cat refglob.'$i')b"
+ '
+done
+
+test_done
diff --git a/t/perf/p3400-rebase.sh b/t/perf/p3400-rebase.sh
index b3e7d525d2..ce271ca4c1 100755
--- a/t/perf/p3400-rebase.sh
+++ b/t/perf/p3400-rebase.sh
@@ -5,7 +5,7 @@ test_description='Tests rebase performance'
test_perf_default_repo
-test_expect_success 'setup' '
+test_expect_success 'setup rebasing on top of a lot of changes' '
git checkout -f -b base &&
git checkout -b to-rebase &&
git checkout -b upstream &&
@@ -33,4 +33,24 @@ test_perf 'rebase on top of a lot of unrelated changes' '
git rebase --onto base HEAD^
'
+test_expect_success 'setup rebasing many changes without split-index' '
+ git config core.splitIndex false &&
+ git checkout -b upstream2 to-rebase &&
+ git checkout -b to-rebase2 upstream
+'
+
+test_perf 'rebase a lot of unrelated changes without split-index' '
+ git rebase --onto upstream2 base &&
+ git rebase --onto base upstream2
+'
+
+test_expect_success 'setup rebasing many changes with split-index' '
+ git config core.splitIndex true
+'
+
+test_perf 'rebase a lot of unrelated changes with split-index' '
+ git rebase --onto upstream2 base &&
+ git rebase --onto base upstream2
+'
+
test_done
diff --git a/t/perf/p4220-log-grep-engines.sh b/t/perf/p4220-log-grep-engines.sh
new file mode 100755
index 0000000000..2bc47ded4d
--- /dev/null
+++ b/t/perf/p4220-log-grep-engines.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+
+test_description="Comparison of git-log's --grep regex engines
+
+Set GIT_PERF_4220_LOG_OPTS in the environment to pass options to
+git-grep. Make sure to include a leading space,
+e.g. GIT_PERF_4220_LOG_OPTS=' -i'. Some options to try:
+
+ -i
+ --invert-grep
+ -i --invert-grep
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+for pattern in \
+ 'how.to' \
+ '^how to' \
+ '[how] to' \
+ '\(e.t[^ ]*\|v.ry\) rare' \
+ 'm\(ú\|u\)lt.b\(æ\|y\)te'
+do
+ for engine in basic extended perl
+ do
+ if test $engine != "basic"
+ then
+ # Poor man's basic -> extended converter.
+ pattern=$(echo $pattern | sed 's/\\//g')
+ fi
+ if test $engine = "perl" && ! test_have_prereq PCRE
+ then
+ prereq="PCRE"
+ else
+ prereq=""
+ fi
+ test_perf $prereq "$engine log$GIT_PERF_4220_LOG_OPTS --grep='$pattern'" "
+ git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4220_LOG_OPTS --grep='$pattern' >'out.$engine' || :
+ "
+ done
+
+ test_expect_success "assert that all engines found the same for$GIT_PERF_4220_LOG_OPTS '$pattern'" '
+ test_cmp out.basic out.extended &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.basic out.perl
+ fi
+ '
+done
+
+test_done
diff --git a/t/perf/p4221-log-grep-engines-fixed.sh b/t/perf/p4221-log-grep-engines-fixed.sh
new file mode 100755
index 0000000000..060971265a
--- /dev/null
+++ b/t/perf/p4221-log-grep-engines-fixed.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+
+test_description="Comparison of git-log's --grep regex engines with -F
+
+Set GIT_PERF_4221_LOG_OPTS in the environment to pass options to
+git-grep. Make sure to include a leading space,
+e.g. GIT_PERF_4221_LOG_OPTS=' -i'. Some options to try:
+
+ -i
+ --invert-grep
+ -i --invert-grep
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+for pattern in 'int' 'uncommon' 'æ'
+do
+ for engine in fixed basic extended perl
+ do
+ if test $engine = "perl" && ! test_have_prereq PCRE
+ then
+ prereq="PCRE"
+ else
+ prereq=""
+ fi
+ test_perf $prereq "$engine log$GIT_PERF_4221_LOG_OPTS --grep='$pattern'" "
+ git -c grep.patternType=$engine log --pretty=format:%h$GIT_PERF_4221_LOG_OPTS --grep='$pattern' >'out.$engine' || :
+ "
+ done
+
+ test_expect_success "assert that all engines found the same for$GIT_PERF_4221_LOG_OPTS '$pattern'" '
+ test_cmp out.fixed out.basic &&
+ test_cmp out.fixed out.extended &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.fixed out.perl
+ fi
+ '
+done
+
+test_done
diff --git a/t/perf/p7820-grep-engines.sh b/t/perf/p7820-grep-engines.sh
new file mode 100755
index 0000000000..62aba19e76
--- /dev/null
+++ b/t/perf/p7820-grep-engines.sh
@@ -0,0 +1,56 @@
+#!/bin/sh
+
+test_description="Comparison of git-grep's regex engines
+
+Set GIT_PERF_7820_GREP_OPTS in the environment to pass options to
+git-grep. Make sure to include a leading space,
+e.g. GIT_PERF_7820_GREP_OPTS=' -i'. Some options to try:
+
+ -i
+ -w
+ -v
+ -vi
+ -vw
+ -viw
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+for pattern in \
+ 'how.to' \
+ '^how to' \
+ '[how] to' \
+ '\(e.t[^ ]*\|v.ry\) rare' \
+ 'm\(ú\|u\)lt.b\(æ\|y\)te'
+do
+ for engine in basic extended perl
+ do
+ if test $engine != "basic"
+ then
+ # Poor man's basic -> extended converter.
+ pattern=$(echo "$pattern" | sed 's/\\//g')
+ fi
+ if test $engine = "perl" && ! test_have_prereq PCRE
+ then
+ prereq="PCRE"
+ else
+ prereq=""
+ fi
+ test_perf $prereq "$engine grep$GIT_PERF_7820_GREP_OPTS '$pattern'" "
+ git -c grep.patternType=$engine grep$GIT_PERF_7820_GREP_OPTS -- '$pattern' >'out.$engine' || :
+ "
+ done
+
+ test_expect_success "assert that all engines found the same for$GIT_PERF_7820_GREP_OPTS '$pattern'" '
+ test_cmp out.basic out.extended &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.basic out.perl
+ fi
+ '
+done
+
+test_done
diff --git a/t/perf/p7821-grep-engines-fixed.sh b/t/perf/p7821-grep-engines-fixed.sh
new file mode 100755
index 0000000000..c7ef1e198f
--- /dev/null
+++ b/t/perf/p7821-grep-engines-fixed.sh
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+test_description="Comparison of git-grep's regex engines with -F
+
+Set GIT_PERF_7821_GREP_OPTS in the environment to pass options to
+git-grep. Make sure to include a leading space,
+e.g. GIT_PERF_7821_GREP_OPTS=' -w'. See p7820-grep-engines.sh for more
+options to try.
+"
+
+. ./perf-lib.sh
+
+test_perf_large_repo
+test_checkout_worktree
+
+for pattern in 'int' 'uncommon' 'æ'
+do
+ for engine in fixed basic extended perl
+ do
+ if test $engine = "perl" && ! test_have_prereq PCRE
+ then
+ prereq="PCRE"
+ else
+ prereq=""
+ fi
+ test_perf $prereq "$engine grep$GIT_PERF_7821_GREP_OPTS $pattern" "
+ git -c grep.patternType=$engine grep$GIT_PERF_7821_GREP_OPTS $pattern >'out.$engine' || :
+ "
+ done
+
+ test_expect_success "assert that all engines found the same for$GIT_PERF_7821_GREP_OPTS $pattern" '
+ test_cmp out.fixed out.basic &&
+ test_cmp out.fixed out.extended &&
+ if test_have_prereq PCRE
+ then
+ test_cmp out.fixed out.perl
+ fi
+ '
+done
+
+test_done
diff --git a/t/perf/perf-lib.sh b/t/perf/perf-lib.sh
index ab4b8b06ae..b6fc880395 100644
--- a/t/perf/perf-lib.sh
+++ b/t/perf/perf-lib.sh
@@ -78,6 +78,10 @@ if test -z "$GIT_PERF_LARGE_REPO"; then
GIT_PERF_LARGE_REPO=$TEST_DIRECTORY/..
fi
+test_perf_do_repo_symlink_config_ () {
+ test_have_prereq SYMLINKS || git config core.symlinks false
+}
+
test_perf_create_repo_from () {
test "$#" = 2 ||
error "bug in the test script: not 2 parameters to test-create-repo"
@@ -102,15 +106,22 @@ test_perf_create_repo_from () {
) &&
(
cd "$repo" &&
- "$MODERN_GIT" init -q && {
- test_have_prereq SYMLINKS ||
- git config core.symlinks false
- } &&
+ "$MODERN_GIT" init -q &&
+ test_perf_do_repo_symlink_config_ &&
mv .git/hooks .git/hooks-disabled 2>/dev/null
) || error "failed to copy repository '$source' to '$repo'"
}
# call at least one of these to establish an appropriately-sized repository
+test_perf_fresh_repo () {
+ repo="${1:-$TRASH_DIRECTORY}"
+ "$MODERN_GIT" init -q "$repo" &&
+ (
+ cd "$repo" &&
+ test_perf_do_repo_symlink_config_
+ )
+}
+
test_perf_default_repo () {
test_perf_create_repo_from "${1:-$TRASH_DIRECTORY}" "$GIT_PERF_REPO"
}
diff --git a/t/perf/run b/t/perf/run
index c788d713ae..beb4acc0e4 100755
--- a/t/perf/run
+++ b/t/perf/run
@@ -24,6 +24,7 @@ run_one_dir () {
unpack_git_rev () {
rev=$1
+ echo "=== Unpacking $rev in build/$rev ==="
mkdir -p build/$rev
(cd "$(git rev-parse --show-cdup)" && git archive --format=tar $rev) |
(cd build/$rev && tar x)
@@ -37,8 +38,16 @@ build_git_rev () {
cp "../../$config" "build/$rev/"
fi
done
- (cd build/$rev && make $GIT_PERF_MAKE_OPTS) ||
- die "failed to build revision '$mydir'"
+ echo "=== Building $rev ==="
+ (
+ cd build/$rev &&
+ if test -n "$GIT_PERF_MAKE_COMMAND"
+ then
+ sh -c "$GIT_PERF_MAKE_COMMAND"
+ else
+ make $GIT_PERF_MAKE_OPTS
+ fi
+ ) || die "failed to build revision '$mydir'"
}
run_dirs_helper () {
diff --git a/t/t0006-date.sh b/t/t0006-date.sh
index c0c910867d..42d4ea61ef 100755
--- a/t/t0006-date.sh
+++ b/t/t0006-date.sh
@@ -53,8 +53,8 @@ check_show unix-local "$TIME" '1466000000'
# arbitrary time absurdly far in the future
FUTURE="5758122296 -0400"
-check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" LONG_IS_64BIT
-check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" LONG_IS_64BIT
+check_show iso "$FUTURE" "2152-06-19 18:24:56 -0400" TIME_IS_64BIT,TIME_T_IS_64BIT
+check_show iso-local "$FUTURE" "2152-06-19 22:24:56 +0000" TIME_IS_64BIT,TIME_T_IS_64BIT
check_parse() {
echo "$1 -> $2" >expect
diff --git a/t/t0061-run-command.sh b/t/t0061-run-command.sh
index 12228b4aa6..e4739170aa 100755
--- a/t/t0061-run-command.sh
+++ b/t/t0061-run-command.sh
@@ -26,6 +26,47 @@ test_expect_success 'run_command can run a command' '
test_cmp empty err
'
+test_expect_success !MINGW 'run_command can run a script without a #! line' '
+ cat >hello <<-\EOF &&
+ cat hello-script
+ EOF
+ chmod +x hello &&
+ test-run-command run-command ./hello >actual 2>err &&
+
+ test_cmp hello-script actual &&
+ test_cmp empty err
+'
+
+test_expect_success 'run_command does not try to execute a directory' '
+ test_when_finished "rm -rf bin1 bin2" &&
+ mkdir -p bin1/greet bin2 &&
+ write_script bin2/greet <<-\EOF &&
+ cat bin2/greet
+ EOF
+
+ PATH=$PWD/bin1:$PWD/bin2:$PATH \
+ test-run-command run-command greet >actual 2>err &&
+ test_cmp bin2/greet actual &&
+ test_cmp empty err
+'
+
+test_expect_success POSIXPERM 'run_command passes over non-executable file' '
+ test_when_finished "rm -rf bin1 bin2" &&
+ mkdir -p bin1 bin2 &&
+ write_script bin1/greet <<-\EOF &&
+ cat bin1/greet
+ EOF
+ chmod -x bin1/greet &&
+ write_script bin2/greet <<-\EOF &&
+ cat bin2/greet
+ EOF
+
+ PATH=$PWD/bin1:$PWD/bin2:$PATH \
+ test-run-command run-command greet >actual 2>err &&
+ test_cmp bin2/greet actual &&
+ test_cmp empty err
+'
+
test_expect_success POSIXPERM 'run_command reports EACCES' '
cat hello-script >hello.sh &&
chmod -x hello.sh &&
diff --git a/t/t1013-read-tree-submodule.sh b/t/t1013-read-tree-submodule.sh
index de1ba02dc5..7019d0a04f 100755
--- a/t/t1013-read-tree-submodule.sh
+++ b/t/t1013-read-tree-submodule.sh
@@ -5,7 +5,6 @@ test_description='read-tree can handle submodules'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
-KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
diff --git a/t/t1300-repo-config.sh b/t/t1300-repo-config.sh
index afcca0d52c..13b7851f7c 100755
--- a/t/t1300-repo-config.sh
+++ b/t/t1300-repo-config.sh
@@ -1539,4 +1539,10 @@ test_expect_success !MINGW '--show-origin blob ref' '
test_cmp expect output
'
+test_expect_success '--local requires a repo' '
+ # we expect 128 to ensure that we do not simply
+ # fail to find anything and return code "1"
+ test_expect_code 128 nongit git config --local foo.bar
+'
+
test_done
diff --git a/t/t1305-config-include.sh b/t/t1305-config-include.sh
index 933915ec06..d9d2f545a4 100755
--- a/t/t1305-config-include.sh
+++ b/t/t1305-config-include.sh
@@ -273,6 +273,29 @@ test_expect_success SYMLINKS 'conditional include, relative path with symlinks'
)
'
+test_expect_success SYMLINKS 'conditional include, gitdir matching symlink' '
+ ln -s foo bar &&
+ (
+ cd bar &&
+ echo "[includeIf \"gitdir:bar/\"]path=bar7" >>.git/config &&
+ echo "[test]seven=7" >.git/bar7 &&
+ echo 7 >expect &&
+ git config test.seven >actual &&
+ test_cmp expect actual
+ )
+'
+
+test_expect_success SYMLINKS 'conditional include, gitdir matching symlink, icase' '
+ (
+ cd bar &&
+ echo "[includeIf \"gitdir/i:BAR/\"]path=bar8" >>.git/config &&
+ echo "[test]eight=8" >.git/bar8 &&
+ echo 8 >expect &&
+ git config test.eight >actual &&
+ test_cmp expect actual
+ )
+'
+
test_expect_success 'include cycles are detected' '
cat >.gitconfig <<-\EOF &&
[test]value = gitconfig
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
index 490521f8cb..e8115df5ba 100755
--- a/t/t1405-main-ref-store.sh
+++ b/t/t1405-main-ref-store.sh
@@ -31,7 +31,7 @@ test_expect_success 'create_symref(FOO, refs/heads/master)' '
test_expect_success 'delete_refs(FOO, refs/tags/new-tag)' '
git rev-parse FOO -- &&
git rev-parse refs/tags/new-tag -- &&
- $RUN delete-refs 0 FOO refs/tags/new-tag &&
+ $RUN delete-refs 0 nothing FOO refs/tags/new-tag &&
test_must_fail git rev-parse FOO -- &&
test_must_fail git rev-parse refs/tags/new-tag --
'
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
index 13b5454c56..c32d4cc465 100755
--- a/t/t1406-submodule-ref-store.sh
+++ b/t/t1406-submodule-ref-store.sh
@@ -31,7 +31,7 @@ test_expect_success 'create_symref() not allowed' '
'
test_expect_success 'delete_refs() not allowed' '
- test_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag
+ test_must_fail $RUN delete-refs 0 nothing FOO refs/tags/new-tag
'
test_expect_success 'rename_refs() not allowed' '
diff --git a/t/t1407-worktree-ref-store.sh b/t/t1407-worktree-ref-store.sh
new file mode 100755
index 0000000000..5df06f3556
--- /dev/null
+++ b/t/t1407-worktree-ref-store.sh
@@ -0,0 +1,52 @@
+#!/bin/sh
+
+test_description='test worktree ref store api'
+
+. ./test-lib.sh
+
+RWT="test-ref-store worktree:wt"
+RMAIN="test-ref-store worktree:main"
+
+test_expect_success 'setup' '
+ test_commit first &&
+ git worktree add -b wt-master wt &&
+ (
+ cd wt &&
+ test_commit second
+ )
+'
+
+test_expect_success 'resolve_ref(<shared-ref>)' '
+ SHA1=`git rev-parse master` &&
+ echo "$SHA1 refs/heads/master 0x0" >expected &&
+ $RWT resolve-ref refs/heads/master 0 >actual &&
+ test_cmp expected actual &&
+ $RMAIN resolve-ref refs/heads/master 0 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'resolve_ref(<per-worktree-ref>)' '
+ SHA1=`git -C wt rev-parse HEAD` &&
+ echo "$SHA1 refs/heads/wt-master 0x1" >expected &&
+ $RWT resolve-ref HEAD 0 >actual &&
+ test_cmp expected actual &&
+
+ SHA1=`git rev-parse HEAD` &&
+ echo "$SHA1 refs/heads/master 0x1" >expected &&
+ $RMAIN resolve-ref HEAD 0 >actual &&
+ test_cmp expected actual
+'
+
+test_expect_success 'create_symref(FOO, refs/heads/master)' '
+ $RWT create-symref FOO refs/heads/master nothing &&
+ echo refs/heads/master >expected &&
+ git -C wt symbolic-ref FOO >actual &&
+ test_cmp expected actual &&
+
+ $RMAIN create-symref FOO refs/heads/wt-master nothing &&
+ echo refs/heads/wt-master >expected &&
+ git symbolic-ref FOO >actual &&
+ test_cmp expected actual
+'
+
+test_done
diff --git a/t/t1601-index-bogus.sh b/t/t1601-index-bogus.sh
new file mode 100755
index 0000000000..73cc9323cd
--- /dev/null
+++ b/t/t1601-index-bogus.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+test_description='test handling of bogus index entries'
+. ./test-lib.sh
+
+test_expect_success 'create tree with null sha1' '
+ tree=$(printf "160000 commit $_z40\\tbroken\\n" | git mktree)
+'
+
+test_expect_success 'read-tree refuses to read null sha1' '
+ test_must_fail git read-tree $tree
+'
+
+test_expect_success 'GIT_ALLOW_NULL_SHA1 overrides refusal' '
+ GIT_ALLOW_NULL_SHA1=1 git read-tree $tree
+'
+
+test_expect_success 'git write-tree refuses to write null sha1' '
+ test_must_fail git write-tree
+'
+
+test_done
diff --git a/t/t2013-checkout-submodule.sh b/t/t2013-checkout-submodule.sh
index e8f70b806f..aa35223369 100755
--- a/t/t2013-checkout-submodule.sh
+++ b/t/t2013-checkout-submodule.sh
@@ -64,7 +64,6 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/
'
KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
-KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1
test_submodule_switch_recursing "git checkout --recurse-submodules"
test_submodule_forced_switch_recursing "git checkout -f --recurse-submodules"
diff --git a/t/t3070-wildmatch.sh b/t/t3070-wildmatch.sh
index ef509df351..7ca69f4bed 100755
--- a/t/t3070-wildmatch.sh
+++ b/t/t3070-wildmatch.sh
@@ -135,7 +135,6 @@ match 1 x '5' '[[:xdigit:]]'
match 1 x 'f' '[[:xdigit:]]'
match 1 x 'D' '[[:xdigit:]]'
match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'
-match 1 x '_' '[[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:graph:][:lower:][:print:][:punct:][:space:][:upper:][:xdigit:]]'
match 1 x '.' '[^[:alnum:][:alpha:][:blank:][:cntrl:][:digit:][:lower:][:space:][:upper:][:xdigit:]]'
match 1 x '5' '[a-c[:digit:]x-z]'
match 1 x 'b' '[a-c[:digit:]x-z]'
diff --git a/t/t3600-rm.sh b/t/t3600-rm.sh
index 5f9913ba33..f8568f8841 100755
--- a/t/t3600-rm.sh
+++ b/t/t3600-rm.sh
@@ -97,9 +97,9 @@ test_expect_success FUNNYNAMES \
embedded'"
test_expect_success SANITY 'Test that "git rm -f" fails if its rm fails' '
+ test_when_finished "chmod 775 ." &&
chmod a-w . &&
- test_must_fail git rm -f baz &&
- chmod 775 .
+ test_must_fail git rm -f baz
'
test_expect_success \
diff --git a/t/t4051-diff-function-context.sh b/t/t4051-diff-function-context.sh
index 6154acb456..3e6b485ecb 100755
--- a/t/t4051-diff-function-context.sh
+++ b/t/t4051-diff-function-context.sh
@@ -72,7 +72,8 @@ test_expect_success 'setup' '
# overlap function context of 1st change and -u context of 2nd change
grep -v "delete me from hello" <"$dir/hello.c" >file.c &&
- sed 2p <"$dir/dummy.c" >>file.c &&
+ sed "2a\\
+ extra line" <"$dir/dummy.c" >>file.c &&
commit_and_tag changed_hello_dummy file.c &&
git checkout initial &&
diff --git a/t/t4060-diff-submodule-option-diff-format.sh b/t/t4060-diff-submodule-option-diff-format.sh
index d4a3ffa69c..33ec26d755 100755
--- a/t/t4060-diff-submodule-option-diff-format.sh
+++ b/t/t4060-diff-submodule-option-diff-format.sh
@@ -775,4 +775,45 @@ test_expect_success 'diff --submodule=diff with moved nested submodule HEAD' '
test_cmp expected actual
'
+test_expect_success 'diff --submodule=diff recurses into nested submodules' '
+ cat >expected <<-EOF &&
+ Submodule sm2 contains modified content
+ Submodule sm2 a5a65c9..280969a:
+ diff --git a/sm2/.gitmodules b/sm2/.gitmodules
+ new file mode 100644
+ index 0000000..3a816b8
+ --- /dev/null
+ +++ b/sm2/.gitmodules
+ @@ -0,0 +1,3 @@
+ +[submodule "nested"]
+ + path = nested
+ + url = ../sm2
+ Submodule nested 0000000...b55928c (new submodule)
+ diff --git a/sm2/nested/file b/sm2/nested/file
+ new file mode 100644
+ index 0000000..ca281f5
+ --- /dev/null
+ +++ b/sm2/nested/file
+ @@ -0,0 +1 @@
+ +nested content
+ diff --git a/sm2/nested/foo8 b/sm2/nested/foo8
+ new file mode 100644
+ index 0000000..db9916b
+ --- /dev/null
+ +++ b/sm2/nested/foo8
+ @@ -0,0 +1 @@
+ +foo8
+ diff --git a/sm2/nested/foo9 b/sm2/nested/foo9
+ new file mode 100644
+ index 0000000..9c3b4f6
+ --- /dev/null
+ +++ b/sm2/nested/foo9
+ @@ -0,0 +1 @@
+ +foo9
+ EOF
+ git diff --submodule=diff >actual 2>err &&
+ test_must_be_empty err &&
+ test_cmp expected actual
+'
+
test_done
diff --git a/t/t4061-diff-indent.sh b/t/t4061-diff-indent.sh
index 556450609b..2affd7a100 100755
--- a/t/t4061-diff-indent.sh
+++ b/t/t4061-diff-indent.sh
@@ -152,26 +152,28 @@ test_expect_success 'prepare' '
EOF
'
+# --- diff tests ----------------------------------------------------------
+
test_expect_success 'diff: ugly spaces' '
- git diff old new -- spaces.txt >out &&
+ git diff --no-indent-heuristic old new -- spaces.txt >out &&
compare_diff spaces-expect out
'
+test_expect_success 'diff: --no-indent-heuristic overrides config' '
+ git -c diff.indentHeuristic=true diff --no-indent-heuristic old new -- spaces.txt >out2 &&
+ compare_diff spaces-expect out2
+'
+
test_expect_success 'diff: nice spaces with --indent-heuristic' '
- git diff --indent-heuristic old new -- spaces.txt >out-compacted &&
+ git -c diff.indentHeuristic=false diff --indent-heuristic old new -- spaces.txt >out-compacted &&
compare_diff spaces-compacted-expect out-compacted
'
-test_expect_success 'diff: nice spaces with diff.indentHeuristic' '
+test_expect_success 'diff: nice spaces with diff.indentHeuristic=true' '
git -c diff.indentHeuristic=true diff old new -- spaces.txt >out-compacted2 &&
compare_diff spaces-compacted-expect out-compacted2
'
-test_expect_success 'diff: --no-indent-heuristic overrides config' '
- git -c diff.indentHeuristic=true diff --no-indent-heuristic old new -- spaces.txt >out2 &&
- compare_diff spaces-expect out2
-'
-
test_expect_success 'diff: --indent-heuristic with --patience' '
git diff --indent-heuristic --patience old new -- spaces.txt >out-compacted3 &&
compare_diff spaces-compacted-expect out-compacted3
@@ -183,7 +185,7 @@ test_expect_success 'diff: --indent-heuristic with --histogram' '
'
test_expect_success 'diff: ugly functions' '
- git diff old new -- functions.c >out &&
+ git diff --no-indent-heuristic old new -- functions.c >out &&
compare_diff functions-expect out
'
@@ -192,25 +194,175 @@ test_expect_success 'diff: nice functions with --indent-heuristic' '
compare_diff functions-compacted-expect out-compacted
'
-test_expect_success 'blame: ugly spaces' '
- git blame old..new -- spaces.txt >out-blame &&
- compare_blame spaces-expect out-blame
-'
+# --- blame tests ---------------------------------------------------------
test_expect_success 'blame: nice spaces with --indent-heuristic' '
git blame --indent-heuristic old..new -- spaces.txt >out-blame-compacted &&
compare_blame spaces-compacted-expect out-blame-compacted
'
-test_expect_success 'blame: nice spaces with diff.indentHeuristic' '
+test_expect_success 'blame: nice spaces with diff.indentHeuristic=true' '
git -c diff.indentHeuristic=true blame old..new -- spaces.txt >out-blame-compacted2 &&
compare_blame spaces-compacted-expect out-blame-compacted2
'
+test_expect_success 'blame: ugly spaces with --no-indent-heuristic' '
+ git blame --no-indent-heuristic old..new -- spaces.txt >out-blame &&
+ compare_blame spaces-expect out-blame
+'
+
+test_expect_success 'blame: ugly spaces with diff.indentHeuristic=false' '
+ git -c diff.indentHeuristic=false blame old..new -- spaces.txt >out-blame2 &&
+ compare_blame spaces-expect out-blame2
+'
+
test_expect_success 'blame: --no-indent-heuristic overrides config' '
- git -c diff.indentHeuristic=true blame --no-indent-heuristic old..new -- spaces.txt >out-blame2 &&
+ git -c diff.indentHeuristic=true blame --no-indent-heuristic old..new -- spaces.txt >out-blame3 &&
git blame old..new -- spaces.txt >out-blame &&
- compare_blame spaces-expect out-blame2
+ compare_blame spaces-expect out-blame3
+'
+
+test_expect_success 'blame: --indent-heuristic overrides config' '
+ git -c diff.indentHeuristic=false blame --indent-heuristic old..new -- spaces.txt >out-blame-compacted3 &&
+ compare_blame spaces-compacted-expect out-blame-compacted2
+'
+
+# --- diff-tree tests -----------------------------------------------------
+
+test_expect_success 'diff-tree: nice spaces with --indent-heuristic' '
+ git diff-tree --indent-heuristic -p old new -- spaces.txt >out-diff-tree-compacted &&
+ compare_diff spaces-compacted-expect out-diff-tree-compacted
+'
+
+test_expect_success 'diff-tree: nice spaces with diff.indentHeuristic=true' '
+ git -c diff.indentHeuristic=true diff-tree -p old new -- spaces.txt >out-diff-tree-compacted2 &&
+ compare_diff spaces-compacted-expect out-diff-tree-compacted2
+'
+
+test_expect_success 'diff-tree: ugly spaces with --no-indent-heuristic' '
+ git diff-tree --no-indent-heuristic -p old new -- spaces.txt >out-diff-tree &&
+ compare_diff spaces-expect out-diff-tree
+'
+
+test_expect_success 'diff-tree: ugly spaces with diff.indentHeuristic=false' '
+ git -c diff.indentHeuristic=false diff-tree -p old new -- spaces.txt >out-diff-tree2 &&
+ compare_diff spaces-expect out-diff-tree2
+'
+
+test_expect_success 'diff-tree: --indent-heuristic overrides config' '
+ git -c diff.indentHeuristic=false diff-tree --indent-heuristic -p old new -- spaces.txt >out-diff-tree-compacted3 &&
+ compare_diff spaces-compacted-expect out-diff-tree-compacted3
+'
+
+test_expect_success 'diff-tree: --no-indent-heuristic overrides config' '
+ git -c diff.indentHeuristic=true diff-tree --no-indent-heuristic -p old new -- spaces.txt >out-diff-tree3 &&
+ compare_diff spaces-expect out-diff-tree3
+'
+
+# --- diff-index tests ----------------------------------------------------
+
+test_expect_success 'diff-index: nice spaces with --indent-heuristic' '
+ git checkout -B diff-index &&
+ git reset --soft HEAD~ &&
+ git diff-index --indent-heuristic -p old -- spaces.txt >out-diff-index-compacted &&
+ compare_diff spaces-compacted-expect out-diff-index-compacted &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-index: nice spaces with diff.indentHeuristic=true' '
+ git checkout -B diff-index &&
+ git reset --soft HEAD~ &&
+ git -c diff.indentHeuristic=true diff-index -p old -- spaces.txt >out-diff-index-compacted2 &&
+ compare_diff spaces-compacted-expect out-diff-index-compacted2 &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-index: ugly spaces with --no-indent-heuristic' '
+ git checkout -B diff-index &&
+ git reset --soft HEAD~ &&
+ git diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index &&
+ compare_diff spaces-expect out-diff-index &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-index: ugly spaces with diff.indentHeuristic=false' '
+ git checkout -B diff-index &&
+ git reset --soft HEAD~ &&
+ git -c diff.indentHeuristic=false diff-index -p old -- spaces.txt >out-diff-index2 &&
+ compare_diff spaces-expect out-diff-index2 &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-index: --indent-heuristic overrides config' '
+ git checkout -B diff-index &&
+ git reset --soft HEAD~ &&
+ git -c diff.indentHeuristic=false diff-index --indent-heuristic -p old -- spaces.txt >out-diff-index-compacted3 &&
+ compare_diff spaces-compacted-expect out-diff-index-compacted3 &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-index: --no-indent-heuristic overrides config' '
+ git checkout -B diff-index &&
+ git reset --soft HEAD~ &&
+ git -c diff.indentHeuristic=true diff-index --no-indent-heuristic -p old -- spaces.txt >out-diff-index3 &&
+ compare_diff spaces-expect out-diff-index3 &&
+ git checkout -f master
+'
+
+# --- diff-files tests ----------------------------------------------------
+
+test_expect_success 'diff-files: nice spaces with --indent-heuristic' '
+ git checkout -B diff-files &&
+ git reset HEAD~ &&
+ git diff-files --indent-heuristic -p spaces.txt >out-diff-files-raw &&
+ grep -v index out-diff-files-raw >out-diff-files-compacted &&
+ compare_diff spaces-compacted-expect out-diff-files-compacted &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-files: nice spaces with diff.indentHeuristic=true' '
+ git checkout -B diff-files &&
+ git reset HEAD~ &&
+ git -c diff.indentHeuristic=true diff-files -p spaces.txt >out-diff-files-raw2 &&
+ grep -v index out-diff-files-raw2 >out-diff-files-compacted2 &&
+ compare_diff spaces-compacted-expect out-diff-files-compacted2 &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-files: ugly spaces with --no-indent-heuristic' '
+ git checkout -B diff-files &&
+ git reset HEAD~ &&
+ git diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw &&
+ grep -v index out-diff-files-raw >out-diff-files &&
+ compare_diff spaces-expect out-diff-files &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-files: ugly spaces with diff.indentHeuristic=false' '
+ git checkout -B diff-files &&
+ git reset HEAD~ &&
+ git -c diff.indentHeuristic=false diff-files -p spaces.txt >out-diff-files-raw2 &&
+ grep -v index out-diff-files-raw2 >out-diff-files &&
+ compare_diff spaces-expect out-diff-files &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-files: --indent-heuristic overrides config' '
+ git checkout -B diff-files &&
+ git reset HEAD~ &&
+ git -c diff.indentHeuristic=false diff-files --indent-heuristic -p spaces.txt >out-diff-files-raw3 &&
+ grep -v index out-diff-files-raw3 >out-diff-files-compacted &&
+ compare_diff spaces-compacted-expect out-diff-files-compacted &&
+ git checkout -f master
+'
+
+test_expect_success 'diff-files: --no-indent-heuristic overrides config' '
+ git checkout -B diff-files &&
+ git reset HEAD~ &&
+ git -c diff.indentHeuristic=true diff-files --no-indent-heuristic -p spaces.txt >out-diff-files-raw4 &&
+ grep -v index out-diff-files-raw4 >out-diff-files &&
+ compare_diff spaces-expect out-diff-files &&
+ git checkout -f master
'
test_done
diff --git a/t/t4063-diff-blobs.sh b/t/t4063-diff-blobs.sh
new file mode 100755
index 0000000000..bc69e26c52
--- /dev/null
+++ b/t/t4063-diff-blobs.sh
@@ -0,0 +1,96 @@
+#!/bin/sh
+
+test_description='test direct comparison of blobs via git-diff'
+. ./test-lib.sh
+
+run_diff () {
+ # use full-index to make it easy to match the index line
+ git diff --full-index "$@" >diff
+}
+
+check_index () {
+ grep "^index $1\\.\\.$2" diff
+}
+
+check_mode () {
+ grep "^old mode $1" diff &&
+ grep "^new mode $2" diff
+}
+
+check_paths () {
+ grep "^diff --git a/$1 b/$2" diff
+}
+
+test_expect_success 'create some blobs' '
+ echo one >one &&
+ echo two >two &&
+ chmod +x two &&
+ git add . &&
+
+ # cover systems where modes are ignored
+ git update-index --chmod=+x two &&
+
+ git commit -m base &&
+
+ sha1_one=$(git rev-parse HEAD:one) &&
+ sha1_two=$(git rev-parse HEAD:two)
+'
+
+test_expect_success 'diff by sha1' '
+ run_diff $sha1_one $sha1_two
+'
+test_expect_success 'index of sha1 diff' '
+ check_index $sha1_one $sha1_two
+'
+test_expect_success 'sha1 diff uses arguments as paths' '
+ check_paths $sha1_one $sha1_two
+'
+test_expect_success 'sha1 diff has no mode change' '
+ ! grep mode diff
+'
+
+test_expect_success 'diff by tree:path (run)' '
+ run_diff HEAD:one HEAD:two
+'
+test_expect_success 'index of tree:path diff' '
+ check_index $sha1_one $sha1_two
+'
+test_expect_success 'tree:path diff uses filenames as paths' '
+ check_paths one two
+'
+test_expect_success 'tree:path diff shows mode change' '
+ check_mode 100644 100755
+'
+
+test_expect_success 'diff by ranged tree:path' '
+ run_diff HEAD:one..HEAD:two
+'
+test_expect_success 'index of ranged tree:path diff' '
+ check_index $sha1_one $sha1_two
+'
+test_expect_success 'ranged tree:path diff uses filenames as paths' '
+ check_paths one two
+'
+test_expect_success 'ranged tree:path diff shows mode change' '
+ check_mode 100644 100755
+'
+
+test_expect_success 'diff blob against file' '
+ run_diff HEAD:one two
+'
+test_expect_success 'index of blob-file diff' '
+ check_index $sha1_one $sha1_two
+'
+test_expect_success 'blob-file diff uses filename as paths' '
+ check_paths one two
+'
+test_expect_success FILEMODE 'blob-file diff shows mode change' '
+ check_mode 100644 100755
+'
+
+test_expect_success 'blob-file diff prefers filename to sha1' '
+ run_diff $sha1_one two &&
+ check_paths two two
+'
+
+test_done
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 1c7d6729c6..66606e7508 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -231,14 +231,47 @@ second
initial
EOF
test_expect_success 'log --invert-grep --grep' '
- git log --pretty="tformat:%s" --invert-grep --grep=th --grep=Sec >actual &&
- test_cmp expect actual
+ # Fixed
+ git -c grep.patternType=fixed log --pretty="tformat:%s" --invert-grep --grep=th --grep=Sec >actual &&
+ test_cmp expect actual &&
+
+ # POSIX basic
+ git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
+ test_cmp expect actual &&
+
+ # POSIX extended
+ git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
+ test_cmp expect actual &&
+
+ # PCRE
+ if test_have_prereq PCRE
+ then
+ git -c grep.patternType=perl log --pretty="tformat:%s" --invert-grep --grep=t[h] --grep=S[e]c >actual &&
+ test_cmp expect actual
+ fi
'
test_expect_success 'log --invert-grep --grep -i' '
echo initial >expect &&
- git log --pretty="tformat:%s" --invert-grep -i --grep=th --grep=Sec >actual &&
- test_cmp expect actual
+
+ # Fixed
+ git -c grep.patternType=fixed log --pretty="tformat:%s" --invert-grep -i --grep=th --grep=Sec >actual &&
+ test_cmp expect actual &&
+
+ # POSIX basic
+ git -c grep.patternType=basic log --pretty="tformat:%s" --invert-grep -i --grep=t[h] --grep=S[e]c >actual &&
+ test_cmp expect actual &&
+
+ # POSIX extended
+ git -c grep.patternType=extended log --pretty="tformat:%s" --invert-grep -i --grep=t[h] --grep=S[e]c >actual &&
+ test_cmp expect actual &&
+
+ # PCRE
+ if test_have_prereq PCRE
+ then
+ git -c grep.patternType=perl log --pretty="tformat:%s" --invert-grep -i --grep=t[h] --grep=S[e]c >actual &&
+ test_cmp expect actual
+ fi
'
test_expect_success 'log --grep option parsing' '
@@ -256,13 +289,53 @@ test_expect_success 'log -i --grep' '
test_expect_success 'log --grep -i' '
echo Second >expect &&
+
+ # Fixed
git log -1 --pretty="tformat:%s" --grep=sec -i >actual &&
- test_cmp expect actual
+ test_cmp expect actual &&
+
+ # POSIX basic
+ git -c grep.patternType=basic log -1 --pretty="tformat:%s" --grep=s[e]c -i >actual &&
+ test_cmp expect actual &&
+
+ # POSIX extended
+ git -c grep.patternType=extended log -1 --pretty="tformat:%s" --grep=s[e]c -i >actual &&
+ test_cmp expect actual &&
+
+ # PCRE
+ if test_have_prereq PCRE
+ then
+ git -c grep.patternType=perl log -1 --pretty="tformat:%s" --grep=s[e]c -i >actual &&
+ test_cmp expect actual
+ fi
'
test_expect_success 'log -F -E --grep=<ere> uses ere' '
echo second >expect &&
- git log -1 --pretty="tformat:%s" -F -E --grep=s.c.nd >actual &&
+ # basic would need \(s\) to do the same
+ git log -1 --pretty="tformat:%s" -F -E --grep="(s).c.nd" >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success PCRE 'log -F -E --perl-regexp --grep=<pcre> uses PCRE' '
+ test_when_finished "rm -rf num_commits" &&
+ git init num_commits &&
+ (
+ cd num_commits &&
+ test_commit 1d &&
+ test_commit 2e
+ ) &&
+
+ # In PCRE \d in [\d] is like saying "0-9", and matches the 2
+ # in 2e...
+ echo 2e >expect &&
+ git -C num_commits log -1 --pretty="tformat:%s" -F -E --perl-regexp --grep="[\d]" >actual &&
+ test_cmp expect actual &&
+
+ # ...in POSIX basic and extended it is the same as [d],
+ # i.e. "d", which matches 1d, but does not match 2e.
+ echo 1d >expect &&
+ git -C num_commits log -1 --pretty="tformat:%s" -F -E --grep="[\d]" >actual &&
test_cmp expect actual
'
@@ -280,6 +353,81 @@ test_expect_success 'log with grep.patternType configuration and command line' '
test_cmp expect actual
'
+test_expect_success 'log with various grep.patternType configurations & command-lines' '
+ git init pattern-type &&
+ (
+ cd pattern-type &&
+ test_commit 1 file A &&
+
+ # The tagname is overridden here because creating a
+ # tag called "(1|2)" as test_commit would otherwise
+ # implicitly do would fail on e.g. MINGW.
+ test_commit "(1|2)" file B 2 &&
+
+ echo "(1|2)" >expect.fixed &&
+ cp expect.fixed expect.basic &&
+ cp expect.fixed expect.extended &&
+ cp expect.fixed expect.perl &&
+
+ # A strcmp-like match with fixed.
+ git -c grep.patternType=fixed log --pretty=tformat:%s \
+ --grep="(1|2)" >actual.fixed &&
+
+ # POSIX basic matches (, | and ) literally.
+ git -c grep.patternType=basic log --pretty=tformat:%s \
+ --grep="(.|.)" >actual.basic &&
+
+ # POSIX extended needs to have | escaped to match it
+ # literally, whereas under basic this is the same as
+ # (|2), i.e. it would also match "1". This test checks
+ # for extended by asserting that it is not matching
+ # what basic would match.
+ git -c grep.patternType=extended log --pretty=tformat:%s \
+ --grep="\|2" >actual.extended &&
+ if test_have_prereq PCRE
+ then
+ # Only PCRE would match [\d]\| with only
+ # "(1|2)" due to [\d]. POSIX basic would match
+ # both it and "1" since similarly to the
+ # extended match above it is the same as
+ # \([\d]\|\). POSIX extended would
+ # match neither.
+ git -c grep.patternType=perl log --pretty=tformat:%s \
+ --grep="[\d]\|" >actual.perl &&
+ test_cmp expect.perl actual.perl
+ fi &&
+ test_cmp expect.fixed actual.fixed &&
+ test_cmp expect.basic actual.basic &&
+ test_cmp expect.extended actual.extended &&
+
+ git log --pretty=tformat:%s -F \
+ --grep="(1|2)" >actual.fixed.short-arg &&
+ git log --pretty=tformat:%s -E \
+ --grep="\|2" >actual.extended.short-arg &&
+ test_cmp expect.fixed actual.fixed.short-arg &&
+ test_cmp expect.extended actual.extended.short-arg &&
+
+ git log --pretty=tformat:%s --fixed-strings \
+ --grep="(1|2)" >actual.fixed.long-arg &&
+ git log --pretty=tformat:%s --basic-regexp \
+ --grep="(.|.)" >actual.basic.long-arg &&
+ git log --pretty=tformat:%s --extended-regexp \
+ --grep="\|2" >actual.extended.long-arg &&
+ if test_have_prereq PCRE
+ then
+ git log --pretty=tformat:%s --perl-regexp \
+ --grep="[\d]\|" >actual.perl.long-arg &&
+ test_cmp expect.perl actual.perl.long-arg
+ else
+ test_must_fail git log --perl-regexp \
+ --grep="[\d]\|"
+ fi &&
+ test_cmp expect.fixed actual.fixed.long-arg &&
+ test_cmp expect.basic actual.basic.long-arg &&
+ test_cmp expect.extended actual.extended.long-arg
+ )
+'
+
cat > expect <<EOF
* Second
* sixth
@@ -399,7 +547,7 @@ cat > expect <<\EOF
| |
| | Merge branch 'side'
| |
-| * commit side
+| * commit tags/side-2
| | Author: A U Thor <author@example.com>
| |
| | side-2
@@ -1392,4 +1540,13 @@ test_expect_success 'log --source paints tag names' '
test_cmp expect actual
'
+test_expect_success 'log --source paints symmetric ranges' '
+ cat >expect <<-\EOF &&
+ 09e12a9 source-b three
+ 8e393e1 source-a two
+ EOF
+ git log --oneline --source source-a...source-b >actual &&
+ test_cmp expect actual
+'
+
test_done
diff --git a/t/t5000-tar-tree.sh b/t/t5000-tar-tree.sh
index 886b6953e4..fe2d4f15a7 100755
--- a/t/t5000-tar-tree.sh
+++ b/t/t5000-tar-tree.sh
@@ -390,7 +390,7 @@ test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our huge size' '
test_cmp expect actual
'
-test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
+test_expect_success TIME_IS_64BIT 'set up repository with far-future commit' '
rm -f .git/index &&
echo content >file &&
git add file &&
@@ -398,11 +398,11 @@ test_expect_success LONG_IS_64BIT 'set up repository with far-future commit' '
git commit -m "tempori parendum"
'
-test_expect_success LONG_IS_64BIT 'generate tar with future mtime' '
+test_expect_success TIME_IS_64BIT 'generate tar with future mtime' '
git archive HEAD >future.tar
'
-test_expect_success TAR_HUGE,LONG_IS_64BIT 'system tar can read our future mtime' '
+test_expect_success TAR_HUGE,TIME_IS_64BIT,TIME_T_IS_64BIT 'system tar can read our future mtime' '
echo 4147 >expect &&
tar_info future.tar | cut -d" " -f2 >actual &&
test_cmp expect actual
diff --git a/t/t5004-archive-corner-cases.sh b/t/t5004-archive-corner-cases.sh
index cca23383c5..f6207f42b5 100755
--- a/t/t5004-archive-corner-cases.sh
+++ b/t/t5004-archive-corner-cases.sh
@@ -27,6 +27,9 @@ check_dir() {
test_cmp expect actual
}
+test_lazy_prereq UNZIP_ZIP64_SUPPORT '
+ "$GIT_UNZIP" -v | grep ZIP64_SUPPORT
+'
# bsdtar/libarchive versions before 3.1.3 consider a tar file with a
# global pax header that is not followed by a file record as corrupt.
@@ -155,4 +158,51 @@ test_expect_success ZIPINFO 'zip archive with many entries' '
test_cmp expect actual
'
+test_expect_success EXPENSIVE,UNZIP,UNZIP_ZIP64_SUPPORT \
+ 'zip archive bigger than 4GB' '
+ # build string containing 65536 characters
+ s=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef &&
+ s=$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s &&
+ s=$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s$s &&
+
+ # create blob with a length of 65536 + 1 bytes
+ blob=$(echo $s | git hash-object -w --stdin) &&
+
+ # create tree containing 65500 entries of that blob
+ for i in $(test_seq 1 65500)
+ do
+ echo "100644 blob $blob $i"
+ done >tree &&
+ tree=$(git mktree <tree) &&
+
+ # zip it, creating an archive a bit bigger than 4GB
+ git archive -0 -o many-big.zip $tree &&
+
+ "$GIT_UNZIP" -t many-big.zip 9999 65500 &&
+ "$GIT_UNZIP" -t many-big.zip
+'
+
+test_expect_success EXPENSIVE,LONG_IS_64BIT,UNZIP,UNZIP_ZIP64_SUPPORT,ZIPINFO \
+ 'zip archive with files bigger than 4GB' '
+ # Pack created with:
+ # dd if=/dev/zero of=file bs=1M count=4100 && git hash-object -w file
+ mkdir -p .git/objects/pack &&
+ (
+ cd .git/objects/pack &&
+ "$GIT_UNZIP" "$TEST_DIRECTORY"/t5004/big-pack.zip
+ ) &&
+ blob=754a93d6fada4c6873360e6cb4b209132271ab0e &&
+ size=$(expr 4100 "*" 1024 "*" 1024) &&
+
+ # create a tree containing the file
+ tree=$(echo "100644 blob $blob big-file" | git mktree) &&
+
+ # zip it, creating an archive with a file bigger than 4GB
+ git archive -o big.zip $tree &&
+
+ "$GIT_UNZIP" -t big.zip &&
+ "$ZIPINFO" big.zip >big.lst &&
+ grep $size big.lst
+'
+
test_done
diff --git a/t/t5004/big-pack.zip b/t/t5004/big-pack.zip
new file mode 100644
index 0000000000..caaf614eee
--- /dev/null
+++ b/t/t5004/big-pack.zip
Binary files differ
diff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh
index 43a672c345..9c68b99251 100755
--- a/t/t5300-pack-object.sh
+++ b/t/t5300-pack-object.sh
@@ -421,6 +421,42 @@ test_expect_success 'index-pack <pack> works in non-repo' '
test_path_is_file foo.idx
'
+test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'index-pack --threads=N or pack.threads=N warns when no pthreads' '
+ test_must_fail git index-pack --threads=2 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 1 warnings &&
+ grep -F "no threads support, ignoring --threads=2" err &&
+
+ test_must_fail git -c pack.threads=2 index-pack 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 1 warnings &&
+ grep -F "no threads support, ignoring pack.threads" err &&
+
+ test_must_fail git -c pack.threads=2 index-pack --threads=4 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 2 warnings &&
+ grep -F "no threads support, ignoring --threads=4" err &&
+ grep -F "no threads support, ignoring pack.threads" err
+'
+
+test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'pack-objects --threads=N or pack.threads=N warns when no pthreads' '
+ git pack-objects --threads=2 --stdout --all </dev/null >/dev/null 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 1 warnings &&
+ grep -F "no threads support, ignoring --threads" err &&
+
+ git -c pack.threads=2 pack-objects --stdout --all </dev/null >/dev/null 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 1 warnings &&
+ grep -F "no threads support, ignoring pack.threads" err &&
+
+ git -c pack.threads=2 pack-objects --threads=4 --stdout --all </dev/null >/dev/null 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 2 warnings &&
+ grep -F "no threads support, ignoring --threads" err &&
+ grep -F "no threads support, ignoring pack.threads" err
+'
+
#
# WARNING!
#
diff --git a/t/t5500-fetch-pack.sh b/t/t5500-fetch-pack.sh
index b5865b385d..80a1a3239a 100755
--- a/t/t5500-fetch-pack.sh
+++ b/t/t5500-fetch-pack.sh
@@ -547,6 +547,41 @@ test_expect_success 'fetch-pack can fetch a raw sha1' '
git fetch-pack hidden $(git -C hidden rev-parse refs/hidden/one)
'
+test_expect_success 'fetch-pack can fetch a raw sha1 that is advertised as a ref' '
+ rm -rf server client &&
+ git init server &&
+ test_commit -C server 1 &&
+
+ git init client &&
+ git -C client fetch-pack ../server \
+ $(git -C server rev-parse refs/heads/master)
+'
+
+test_expect_success 'fetch-pack can fetch a raw sha1 overlapping a named ref' '
+ rm -rf server client &&
+ git init server &&
+ test_commit -C server 1 &&
+ test_commit -C server 2 &&
+
+ git init client &&
+ git -C client fetch-pack ../server \
+ $(git -C server rev-parse refs/tags/1) refs/tags/1
+'
+
+test_expect_success 'fetch-pack cannot fetch a raw sha1 that is not advertised as a ref' '
+ rm -rf server &&
+
+ git init server &&
+ test_commit -C server 5 &&
+ git -C server tag -d 5 &&
+ test_commit -C server 6 &&
+
+ git init client &&
+ test_must_fail git -C client fetch-pack ../server \
+ $(git -C server rev-parse refs/heads/master^) 2>err &&
+ test_i18ngrep "Server does not allow request for unadvertised object" err
+'
+
check_prot_path () {
cat >expected <<-EOF &&
Diag: url=$1
diff --git a/t/t5520-pull.sh b/t/t5520-pull.sh
index 17f4d0fe4e..f15f7a3329 100755
--- a/t/t5520-pull.sh
+++ b/t/t5520-pull.sh
@@ -272,6 +272,24 @@ test_expect_success '--rebase fast forward' '
test_cmp reflog.expected reflog.fuzzy
'
+test_expect_success '--rebase --autostash fast forward' '
+ test_when_finished "
+ git reset --hard
+ git checkout to-rebase
+ git branch -D to-rebase-ff
+ git branch -D behind" &&
+ git branch behind &&
+ git checkout -b to-rebase-ff &&
+ echo another modification >>file &&
+ git add file &&
+ git commit -m mod &&
+
+ git checkout behind &&
+ echo dirty >file &&
+ git pull --rebase --autostash . to-rebase-ff &&
+ test "$(git rev-parse HEAD)" = "$(git rev-parse to-rebase-ff)"
+'
+
test_expect_success '--rebase with conflicts shows advice' '
test_when_finished "git rebase --abort; git checkout -f to-rebase" &&
git checkout -b seq &&
diff --git a/t/t5545-push-options.sh b/t/t5545-push-options.sh
index f9232f5d0f..90a4b0d2fe 100755
--- a/t/t5545-push-options.sh
+++ b/t/t5545-push-options.sh
@@ -3,8 +3,6 @@
test_description='pushing to a repository using push options'
. ./test-lib.sh
-. "$TEST_DIRECTORY"/lib-httpd.sh
-start_httpd
mk_repo_pair () {
rm -rf workbench upstream &&
@@ -102,46 +100,6 @@ test_expect_success 'two push options work' '
test_cmp expect upstream/.git/hooks/post-receive.push_options
'
-test_expect_success 'push option denied properly by http server' '
- test_when_finished "rm -rf test_http_clone" &&
- test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
- mk_repo_pair &&
- git -C upstream config receive.advertisePushOptions false &&
- git -C upstream config http.receivepack true &&
- cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
- git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
- test_commit -C test_http_clone one &&
- test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual &&
- test_i18ngrep "the receiving end does not support push options" actual &&
- git -C test_http_clone push origin master
-'
-
-test_expect_success 'push options work properly across http' '
- test_when_finished "rm -rf test_http_clone" &&
- test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
- mk_repo_pair &&
- git -C upstream config receive.advertisePushOptions true &&
- git -C upstream config http.receivepack true &&
- cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
- git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
-
- test_commit -C test_http_clone one &&
- git -C test_http_clone push origin master &&
- git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect &&
- git -C test_http_clone rev-parse --verify master >actual &&
- test_cmp expect actual &&
-
- test_commit -C test_http_clone two &&
- git -C test_http_clone push --push-option=asdf --push-option="more structured text" origin master &&
- printf "asdf\nmore structured text\n" >expect &&
- test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options &&
- test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/post-receive.push_options &&
-
- git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect &&
- git -C test_http_clone rev-parse --verify master >actual &&
- test_cmp expect actual
-'
-
test_expect_success 'push options and submodules' '
test_when_finished "rm -rf parent" &&
test_when_finished "rm -rf parent_upstream" &&
@@ -182,6 +140,49 @@ test_expect_success 'push options and submodules' '
test_cmp expect parent_upstream/.git/hooks/post-receive.push_options
'
+. "$TEST_DIRECTORY"/lib-httpd.sh
+start_httpd
+
+test_expect_success 'push option denied properly by http server' '
+ test_when_finished "rm -rf test_http_clone" &&
+ test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions false &&
+ git -C upstream config http.receivepack true &&
+ cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
+ git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
+ test_commit -C test_http_clone one &&
+ test_must_fail git -C test_http_clone push --push-option=asdf origin master 2>actual &&
+ test_i18ngrep "the receiving end does not support push options" actual &&
+ git -C test_http_clone push origin master
+'
+
+test_expect_success 'push options work properly across http' '
+ test_when_finished "rm -rf test_http_clone" &&
+ test_when_finished "rm -rf \"$HTTPD_DOCUMENT_ROOT_PATH\"/upstream.git" &&
+ mk_repo_pair &&
+ git -C upstream config receive.advertisePushOptions true &&
+ git -C upstream config http.receivepack true &&
+ cp -R upstream/.git "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git &&
+ git clone "$HTTPD_URL"/smart/upstream test_http_clone &&
+
+ test_commit -C test_http_clone one &&
+ git -C test_http_clone push origin master &&
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect &&
+ git -C test_http_clone rev-parse --verify master >actual &&
+ test_cmp expect actual &&
+
+ test_commit -C test_http_clone two &&
+ git -C test_http_clone push --push-option=asdf --push-option="more structured text" origin master &&
+ printf "asdf\nmore structured text\n" >expect &&
+ test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/pre-receive.push_options &&
+ test_cmp expect "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git/hooks/post-receive.push_options &&
+
+ git -C "$HTTPD_DOCUMENT_ROOT_PATH"/upstream.git rev-parse --verify master >expect &&
+ git -C test_http_clone rev-parse --verify master >actual &&
+ test_cmp expect actual
+'
+
stop_httpd
test_done
diff --git a/t/t5550-http-fetch-dumb.sh b/t/t5550-http-fetch-dumb.sh
index 87308cdced..8552184e74 100755
--- a/t/t5550-http-fetch-dumb.sh
+++ b/t/t5550-http-fetch-dumb.sh
@@ -20,8 +20,9 @@ test_expect_success 'create http-accessible bare repository with loose objects'
(cd "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
git config core.bare true &&
mkdir -p hooks &&
- echo "exec git update-server-info" >hooks/post-update &&
- chmod +x hooks/post-update &&
+ write_script "hooks/post-update" <<-\EOF &&
+ exec git update-server-info
+ EOF
hooks/post-update
) &&
git remote add public "$HTTPD_DOCUMENT_ROOT_PATH/repo.git" &&
diff --git a/t/t5612-clone-refspec.sh b/t/t5612-clone-refspec.sh
index 7ace2535c8..fac5a73851 100755
--- a/t/t5612-clone-refspec.sh
+++ b/t/t5612-clone-refspec.sh
@@ -17,13 +17,20 @@ test_expect_success 'setup' '
echo four >file &&
git commit -a -m four &&
git checkout master &&
+ git tag five &&
# default clone
git clone . dir_all &&
+ # default clone --no-tags
+ git clone --no-tags . dir_all_no_tags &&
+
# default --single that follows HEAD=master
git clone --single-branch . dir_master &&
+ # default --single that follows HEAD=master with no tags
+ git clone --single-branch --no-tags . dir_master_no_tags &&
+
# default --single that follows HEAD=side
git checkout side &&
git clone --single-branch . dir_side &&
@@ -45,6 +52,9 @@ test_expect_success 'setup' '
# explicit --single with tag
git clone --single-branch --branch two . dir_tag &&
+ # explicit --single with tag and --no-tags
+ git clone --single-branch --no-tags --branch two . dir_tag_no_tags &&
+
# advance both "master" and "side" branches
git checkout side &&
echo five >file &&
@@ -59,7 +69,8 @@ test_expect_success 'setup' '
test_expect_success 'by default all branches will be kept updated' '
(
- cd dir_all && git fetch &&
+ cd dir_all &&
+ git fetch &&
git for-each-ref refs/remotes/origin |
sed -e "/HEAD$/d" \
-e "s|/remotes/origin/|/heads/|" >../actual
@@ -71,28 +82,82 @@ test_expect_success 'by default all branches will be kept updated' '
test_expect_success 'by default no tags will be kept updated' '
(
- cd dir_all && git fetch &&
+ cd dir_all &&
+ git fetch &&
git for-each-ref refs/tags >../actual
) &&
git for-each-ref refs/tags >expect &&
- test_must_fail test_cmp expect actual
+ test_must_fail test_cmp expect actual &&
+ test_line_count = 2 actual
+'
+
+test_expect_success 'clone with --no-tags' '
+ (
+ cd dir_all_no_tags &&
+ git fetch &&
+ git for-each-ref refs/tags >../actual
+ ) &&
+ >expect &&
+ test_cmp expect actual
'
test_expect_success '--single-branch while HEAD pointing at master' '
(
- cd dir_master && git fetch &&
+ cd dir_master &&
+ git fetch &&
git for-each-ref refs/remotes/origin |
sed -e "/HEAD$/d" \
-e "s|/remotes/origin/|/heads/|" >../actual
) &&
# only follow master
git for-each-ref refs/heads/master >expect &&
- test_cmp expect actual
+ # get & check latest tags
+ test_cmp expect actual &&
+ (
+ cd dir_master &&
+ git fetch --tags &&
+ git for-each-ref refs/tags >../actual
+ ) &&
+ git for-each-ref refs/tags >expect &&
+ test_cmp expect actual &&
+ test_line_count = 2 actual
+'
+
+test_expect_success '--single-branch while HEAD pointing at master and --no-tags' '
+ (
+ cd dir_master_no_tags &&
+ git fetch &&
+ git for-each-ref refs/remotes/origin |
+ sed -e "/HEAD$/d" \
+ -e "s|/remotes/origin/|/heads/|" >../actual
+ ) &&
+ # only follow master
+ git for-each-ref refs/heads/master >expect &&
+ test_cmp expect actual &&
+ # get tags (noop)
+ (
+ cd dir_master_no_tags &&
+ git fetch &&
+ git for-each-ref refs/tags >../actual
+ ) &&
+ >expect &&
+ test_cmp expect actual &&
+ test_line_count = 0 actual &&
+ # get tags with --tags overrides tagOpt
+ (
+ cd dir_master_no_tags &&
+ git fetch --tags &&
+ git for-each-ref refs/tags >../actual
+ ) &&
+ git for-each-ref refs/tags >expect &&
+ test_cmp expect actual &&
+ test_line_count = 2 actual
'
test_expect_success '--single-branch while HEAD pointing at side' '
(
- cd dir_side && git fetch &&
+ cd dir_side &&
+ git fetch &&
git for-each-ref refs/remotes/origin |
sed -e "/HEAD$/d" \
-e "s|/remotes/origin/|/heads/|" >../actual
@@ -104,7 +169,8 @@ test_expect_success '--single-branch while HEAD pointing at side' '
test_expect_success '--single-branch with explicit --branch side' '
(
- cd dir_side2 && git fetch &&
+ cd dir_side2 &&
+ git fetch &&
git for-each-ref refs/remotes/origin |
sed -e "/HEAD$/d" \
-e "s|/remotes/origin/|/heads/|" >../actual
@@ -116,16 +182,29 @@ test_expect_success '--single-branch with explicit --branch side' '
test_expect_success '--single-branch with explicit --branch with tag fetches updated tag' '
(
- cd dir_tag && git fetch &&
+ cd dir_tag &&
+ git fetch &&
git for-each-ref refs/tags >../actual
) &&
git for-each-ref refs/tags >expect &&
test_cmp expect actual
'
+test_expect_success '--single-branch with explicit --branch with tag fetches updated tag despite --no-tags' '
+ (
+ cd dir_tag_no_tags &&
+ git fetch &&
+ git for-each-ref refs/tags >../actual
+ ) &&
+ git for-each-ref refs/tags/two >expect &&
+ test_cmp expect actual &&
+ test_line_count = 1 actual
+'
+
test_expect_success '--single-branch with --mirror' '
(
- cd dir_mirror && git fetch &&
+ cd dir_mirror &&
+ git fetch &&
git for-each-ref refs > ../actual
) &&
git for-each-ref refs >expect &&
@@ -134,7 +213,8 @@ test_expect_success '--single-branch with --mirror' '
test_expect_success '--single-branch with explicit --branch and --mirror' '
(
- cd dir_mirror_side && git fetch &&
+ cd dir_mirror_side &&
+ git fetch &&
git for-each-ref refs > ../actual
) &&
git for-each-ref refs >expect &&
@@ -143,7 +223,8 @@ test_expect_success '--single-branch with explicit --branch and --mirror' '
test_expect_success '--single-branch with detached' '
(
- cd dir_detached && git fetch &&
+ cd dir_detached &&
+ git fetch &&
git for-each-ref refs/remotes/origin |
sed -e "/HEAD$/d" \
-e "s|/remotes/origin/|/heads/|" >../actual
diff --git a/t/t5614-clone-submodules.sh b/t/t5614-clone-submodules-shallow.sh
index a87d329656..a87d329656 100755
--- a/t/t5614-clone-submodules.sh
+++ b/t/t5614-clone-submodules-shallow.sh
diff --git a/t/t6134-pathspec-in-submodule.sh b/t/t6134-pathspec-in-submodule.sh
index 99a8982ab1..c670668409 100755
--- a/t/t6134-pathspec-in-submodule.sh
+++ b/t/t6134-pathspec-in-submodule.sh
@@ -24,13 +24,9 @@ test_expect_success 'error message for path inside submodule' '
test_i18ncmp expect actual
'
-cat <<EOF >expect
-fatal: Pathspec '.' is in submodule 'sub'
-EOF
-
test_expect_success 'error message for path inside submodule from within submodule' '
test_must_fail git -C sub add . 2>actual &&
- test_i18ncmp expect actual
+ test_i18ngrep "in unpopulated submodule" actual
'
test_done
diff --git a/t/t7008-grep-binary.sh b/t/t7008-grep-binary.sh
index 9c9c378119..615e7e0162 100755
--- a/t/t7008-grep-binary.sh
+++ b/t/t7008-grep-binary.sh
@@ -4,8 +4,43 @@ test_description='git grep in binary files'
. ./test-lib.sh
+nul_match () {
+ matches=$1
+ flags=$2
+ pattern=$3
+ pattern_human=$(echo "$pattern" | sed 's/Q/<NUL>/g')
+
+ if test "$matches" = 1
+ then
+ test_expect_success "git grep -f f $flags '$pattern_human' a" "
+ printf '$pattern' | q_to_nul >f &&
+ git grep -f f $flags a
+ "
+ elif test "$matches" = 0
+ then
+ test_expect_success "git grep -f f $flags '$pattern_human' a" "
+ printf '$pattern' | q_to_nul >f &&
+ test_must_fail git grep -f f $flags a
+ "
+ elif test "$matches" = T1
+ then
+ test_expect_failure "git grep -f f $flags '$pattern_human' a" "
+ printf '$pattern' | q_to_nul >f &&
+ git grep -f f $flags a
+ "
+ elif test "$matches" = T0
+ then
+ test_expect_failure "git grep -f f $flags '$pattern_human' a" "
+ printf '$pattern' | q_to_nul >f &&
+ test_must_fail git grep -f f $flags a
+ "
+ else
+ test_expect_success "PANIC: Test framework error. Unknown matches value $matches" 'false'
+ fi
+}
+
test_expect_success 'setup' "
- echo 'binaryQfile' | q_to_nul >a &&
+ echo 'binaryQfileQm[*]cQ*æQð' | q_to_nul >a &&
git add a &&
git commit -m.
"
@@ -69,35 +104,71 @@ test_expect_failure 'git grep .fi a' '
git grep .fi a
'
-test_expect_success 'git grep -F y<NUL>f a' "
- printf 'yQf' | q_to_nul >f &&
- git grep -f f -F a
-"
-
-test_expect_success 'git grep -F y<NUL>x a' "
- printf 'yQx' | q_to_nul >f &&
- test_must_fail git grep -f f -F a
-"
-
-test_expect_success 'git grep -Fi Y<NUL>f a' "
- printf 'YQf' | q_to_nul >f &&
- git grep -f f -Fi a
-"
-
-test_expect_success 'git grep -Fi Y<NUL>x a' "
- printf 'YQx' | q_to_nul >f &&
- test_must_fail git grep -f f -Fi a
-"
-
-test_expect_success 'git grep y<NUL>f a' "
- printf 'yQf' | q_to_nul >f &&
- git grep -f f a
-"
-
-test_expect_success 'git grep y<NUL>x a' "
- printf 'yQx' | q_to_nul >f &&
- test_must_fail git grep -f f a
-"
+nul_match 1 '-F' 'yQf'
+nul_match 0 '-F' 'yQx'
+nul_match 1 '-Fi' 'YQf'
+nul_match 0 '-Fi' 'YQx'
+nul_match 1 '' 'yQf'
+nul_match 0 '' 'yQx'
+nul_match 1 '' 'æQð'
+nul_match 1 '-F' 'eQm[*]c'
+nul_match 1 '-Fi' 'EQM[*]C'
+
+# Regex patterns that would match but shouldn't with -F
+nul_match 0 '-F' 'yQ[f]'
+nul_match 0 '-F' '[y]Qf'
+nul_match 0 '-Fi' 'YQ[F]'
+nul_match 0 '-Fi' '[Y]QF'
+nul_match 0 '-F' 'æQ[ð]'
+nul_match 0 '-F' '[æ]Qð'
+nul_match 0 '-Fi' 'ÆQ[Ã]'
+nul_match 0 '-Fi' '[Æ]QÃ'
+
+# kwset is disabled on -i & non-ASCII. No way to match non-ASCII \0
+# patterns case-insensitively.
+nul_match T1 '-i' 'ÆQÃ'
+
+# \0 implicitly disables regexes. This is an undocumented internal
+# limitation.
+nul_match T1 '' 'yQ[f]'
+nul_match T1 '' '[y]Qf'
+nul_match T1 '-i' 'YQ[F]'
+nul_match T1 '-i' '[Y]Qf'
+nul_match T1 '' 'æQ[ð]'
+nul_match T1 '' '[æ]Qð'
+nul_match T1 '-i' 'ÆQ[Ã]'
+
+# ... because of \0 implicitly disabling regexes regexes that
+# should/shouldn't match don't do the right thing.
+nul_match T1 '' 'eQm.*cQ'
+nul_match T1 '-i' 'EQM.*cQ'
+nul_match T0 '' 'eQm[*]c'
+nul_match T0 '-i' 'EQM[*]C'
+
+# Due to the REG_STARTEND extension when kwset() is disabled on -i &
+# non-ASCII the string will be matched in its entirety, but the
+# pattern will be cut off at the first \0.
+nul_match 0 '-i' 'NOMATCHQð'
+nul_match T0 '-i' '[Æ]QNOMATCH'
+nul_match T0 '-i' '[æ]QNOMATCH'
+# Matches, but for the wrong reasons, just stops at [æ]
+nul_match 1 '-i' '[Æ]Qð'
+nul_match 1 '-i' '[æ]Qð'
+
+# Ensure that the matcher doesn't regress to something that stops at
+# \0
+nul_match 0 '-F' 'yQ[f]'
+nul_match 0 '-Fi' 'YQ[F]'
+nul_match 0 '' 'yQNOMATCH'
+nul_match 0 '' 'QNOMATCH'
+nul_match 0 '-i' 'YQNOMATCH'
+nul_match 0 '-i' 'QNOMATCH'
+nul_match 0 '-F' 'æQ[ð]'
+nul_match 0 '-Fi' 'ÆQ[Ã]'
+nul_match 0 '' 'yQNÓMATCH'
+nul_match 0 '' 'QNÓMATCH'
+nul_match 0 '-i' 'YQNÓMATCH'
+nul_match 0 '-i' 'QNÓMATCH'
test_expect_success 'grep respects binary diff attribute' '
echo text >t &&
@@ -162,7 +233,7 @@ test_expect_success 'grep does not honor textconv' '
'
test_expect_success 'grep --textconv honors textconv' '
- echo "a:binaryQfile" >expect &&
+ echo "a:binaryQfileQm[*]cQ*æQð" >expect &&
git grep --textconv Qfile >actual &&
test_cmp expect actual
'
@@ -172,7 +243,7 @@ test_expect_success 'grep --no-textconv does not honor textconv' '
'
test_expect_success 'grep --textconv blob honors textconv' '
- echo "HEAD:a:binaryQfile" >expect &&
+ echo "HEAD:a:binaryQfileQm[*]cQ*æQð" >expect &&
git grep --textconv Qfile HEAD:a >actual &&
test_cmp expect actual
'
diff --git a/t/t7009-filter-branch-null-sha1.sh b/t/t7009-filter-branch-null-sha1.sh
index c27f90f285..a8d9ec4987 100755
--- a/t/t7009-filter-branch-null-sha1.sh
+++ b/t/t7009-filter-branch-null-sha1.sh
@@ -31,6 +31,12 @@ test_expect_success 'setup: bring HEAD and index in sync' '
git commit -a -m "back to normal"
'
+test_expect_success 'noop filter-branch complains' '
+ test_must_fail git filter-branch \
+ --force --prune-empty \
+ --index-filter "true"
+'
+
test_expect_success 'filter commands are still checked' '
test_must_fail git filter-branch \
--force --prune-empty \
diff --git a/t/t7061-wtstatus-ignore.sh b/t/t7061-wtstatus-ignore.sh
index cdc0747bf0..fc6013ba3c 100755
--- a/t/t7061-wtstatus-ignore.sh
+++ b/t/t7061-wtstatus-ignore.sh
@@ -9,6 +9,7 @@ cat >expected <<\EOF
?? actual
?? expected
?? untracked/
+!! untracked/ignored
EOF
test_expect_success 'status untracked directory with --ignored' '
diff --git a/t/t7063-status-untracked-cache.sh b/t/t7063-status-untracked-cache.sh
index 0667bd9dd3..e5fb892f95 100755
--- a/t/t7063-status-untracked-cache.sh
+++ b/t/t7063-status-untracked-cache.sh
@@ -661,4 +661,26 @@ test_expect_success 'test ident field is working' '
test_i18ncmp ../expect ../err
'
+test_expect_success 'untracked cache survives a checkout' '
+ git commit --allow-empty -m empty &&
+ test-dump-untracked-cache >../before &&
+ test_when_finished "git checkout master" &&
+ git checkout -b other_branch &&
+ test-dump-untracked-cache >../after &&
+ test_cmp ../before ../after &&
+ test_commit test &&
+ test-dump-untracked-cache >../before &&
+ git checkout master &&
+ test-dump-untracked-cache >../after &&
+ test_cmp ../before ../after
+'
+
+test_expect_success 'untracked cache survives a commit' '
+ test-dump-untracked-cache >../before &&
+ git add done/two &&
+ git commit -m commit &&
+ test-dump-untracked-cache >../after &&
+ test_cmp ../before ../after
+'
+
test_done
diff --git a/t/t7112-reset-submodule.sh b/t/t7112-reset-submodule.sh
index 2eda6adeb1..f86ccdf215 100755
--- a/t/t7112-reset-submodule.sh
+++ b/t/t7112-reset-submodule.sh
@@ -5,6 +5,14 @@ test_description='reset can handle submodules'
. ./test-lib.sh
. "$TEST_DIRECTORY"/lib-submodule-update.sh
+KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1
+KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
+KNOWN_FAILURE_SUBMODULE_OVERWRITE_IGNORED_UNTRACKED=1
+
+test_submodule_switch_recursing "git reset --recurse-submodules --keep"
+
+test_submodule_forced_switch_recursing "git reset --hard --recurse-submodules"
+
test_submodule_switch "git reset --keep"
test_submodule_switch "git reset --merge"
diff --git a/t/t7300-clean.sh b/t/t7300-clean.sh
index b89fd2a6ad..7b36954d63 100755
--- a/t/t7300-clean.sh
+++ b/t/t7300-clean.sh
@@ -653,4 +653,20 @@ test_expect_success 'git clean -d respects pathspecs (pathspec is prefix of dir)
test_path_is_dir foobar
'
+test_expect_success 'git clean -d skips untracked dirs containing ignored files' '
+ echo /foo/bar >.gitignore &&
+ echo ignoreme >>.gitignore &&
+ rm -rf foo &&
+ mkdir -p foo/a/aa/aaa foo/b/bb/bbb &&
+ touch foo/bar foo/baz foo/a/aa/ignoreme foo/b/ignoreme foo/b/bb/1 foo/b/bb/2 &&
+ git clean -df &&
+ test_path_is_dir foo &&
+ test_path_is_file foo/bar &&
+ test_path_is_missing foo/baz &&
+ test_path_is_file foo/a/aa/ignoreme &&
+ test_path_is_missing foo/a/aa/aaa &&
+ test_path_is_file foo/b/ignoreme &&
+ test_path_is_missing foo/b/bb
+'
+
test_done
diff --git a/t/t7508-status.sh b/t/t7508-status.sh
index 5edcc6edfe..79427840a4 100755
--- a/t/t7508-status.sh
+++ b/t/t7508-status.sh
@@ -32,6 +32,17 @@ test_expect_success 'commit -h in broken repository' '
test_i18ngrep "[Uu]sage" broken/usage
'
+test_expect_success 'create upstream branch' '
+ git checkout -b upstream &&
+ test_commit upstream1 &&
+ test_commit upstream2 &&
+ # leave the first commit on master as root because several
+ # tests depend on this case; for our upstream we only
+ # care about commit counts anyway, so a totally divergent
+ # history is OK
+ git checkout --orphan master
+'
+
test_expect_success 'setup' '
: >tracked &&
: >modified &&
@@ -53,7 +64,9 @@ test_expect_success 'setup' '
echo 1 >dir1/modified &&
echo 2 >dir2/modified &&
echo 3 >dir2/added &&
- git add dir2/added
+ git add dir2/added &&
+
+ git branch --set-upstream-to=upstream
'
test_expect_success 'status (1)' '
@@ -75,6 +88,10 @@ EOF
test_expect_success 'status --column' '
cat >expect <<\EOF &&
# On branch master
+# Your branch and '\''upstream'\'' have diverged,
+# and have 1 and 2 different commits each, respectively.
+# (use "git pull" to merge the remote branch into yours)
+#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
@@ -105,6 +122,10 @@ test_expect_success 'status --column status.displayCommentPrefix=false' '
cat >expect <<\EOF
# On branch master
+# Your branch and 'upstream' have diverged,
+# and have 1 and 2 different commits each, respectively.
+# (use "git pull" to merge the remote branch into yours)
+#
# Changes to be committed:
# (use "git reset HEAD <file>..." to unstage)
#
@@ -178,6 +199,9 @@ test_expect_success 'commit ignores status.displayCommentPrefix=false in COMMIT_
cat >expect <<\EOF
On branch master
+Your branch and 'upstream' have diverged,
+and have 1 and 2 different commits each, respectively.
+
Changes to be committed:
new file: dir2/added
@@ -248,6 +272,10 @@ test_expect_success 'status with gitignore' '
cat >expect <<\EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -313,6 +341,10 @@ test_expect_success 'status with gitignore (nothing untracked)' '
cat >expect <<\EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -348,7 +380,7 @@ output*
EOF
cat >expect <<\EOF
-## master
+## master...upstream [ahead 1, behind 2]
M dir1/modified
A dir2/added
?? dir1/untracked
@@ -382,6 +414,10 @@ test_expect_success 'setup dir3' '
test_expect_success 'status -uno' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -408,6 +444,9 @@ test_expect_success 'status (status.showUntrackedFiles no)' '
test_expect_success 'status -uno (advice.statusHints false)' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+
Changes to be committed:
new file: dir2/added
@@ -439,6 +478,10 @@ test_expect_success 'status -s (status.showUntrackedFiles no)' '
test_expect_success 'status -unormal' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -493,6 +536,10 @@ test_expect_success 'status -s (status.showUntrackedFiles normal)' '
test_expect_success 'status -uall' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -552,6 +599,10 @@ test_expect_success 'status -s (status.showUntrackedFiles all)' '
test_expect_success 'status with relative paths' '
cat >expect <<\EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -610,13 +661,19 @@ test_expect_success 'status --porcelain ignores relative paths setting' '
test_expect_success 'setup unique colors' '
git config status.color.untracked blue &&
- git config status.color.branch green
+ git config status.color.branch green &&
+ git config status.color.localBranch yellow &&
+ git config status.color.remoteBranch cyan
'
test_expect_success 'status with color.ui' '
cat >expect <<\EOF &&
On branch <GREEN>master<RESET>
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -675,7 +732,7 @@ test_expect_success 'status -s with color.status' '
'
cat >expect <<\EOF
-## <GREEN>master<RESET>
+## <YELLOW>master<RESET>...<CYAN>upstream<RESET> [ahead <YELLOW>1<RESET>, behind <CYAN>2<RESET>]
<RED>M<RESET> dir1/modified
<GREEN>A<RESET> dir2/added
<BLUE>??<RESET> dir1/untracked
@@ -726,7 +783,7 @@ test_expect_success 'status --porcelain respects -b' '
git status --porcelain -b >output &&
{
- echo "## master" &&
+ echo "## master...upstream [ahead 1, behind 2]" &&
cat expect
} >tmp &&
mv tmp expect &&
@@ -739,6 +796,10 @@ test_expect_success 'status --porcelain respects -b' '
test_expect_success 'status without relative paths' '
cat >expect <<\EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -785,6 +846,10 @@ test_expect_success 'status -s without relative paths' '
test_expect_success 'dry-run of partial commit excluding new file in index' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -825,6 +890,10 @@ test_expect_success 'setup status submodule summary' '
test_expect_success 'status submodule summary is disabled by default' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -881,6 +950,10 @@ head=$(cd sm && git rev-parse --short=7 --verify HEAD)
test_expect_success 'status submodule summary' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 1 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -939,6 +1012,10 @@ test_expect_success 'status -s submodule summary' '
test_expect_success 'status submodule summary (clean submodule): commit' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
@@ -985,6 +1062,10 @@ test_expect_success 'status -z implies porcelain' '
test_expect_success 'commit --dry-run submodule summary (--amend)' '
cat >expect <<EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD^1 <file>..." to unstage)
@@ -1038,6 +1119,10 @@ touch .gitmodules
test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' '
cat > expect << EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -1146,6 +1231,10 @@ test_expect_success '.git/config ignore=dirty suppresses submodules with modifie
test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" '
cat > expect << EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -1202,6 +1291,10 @@ head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --
test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" '
cat > expect << EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -1282,6 +1375,10 @@ test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary
cat > expect << EOF
; On branch master
+; Your branch and 'upstream' have diverged,
+; and have 2 and 2 different commits each, respectively.
+; (use "git pull" to merge the remote branch into yours)
+;
; Changes to be committed:
; (use "git reset HEAD <file>..." to unstage)
;
@@ -1329,6 +1426,10 @@ test_expect_success "status (core.commentchar with two chars with submodule summ
test_expect_success "--ignore-submodules=all suppresses submodule summary" '
cat > expect << EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
@@ -1353,6 +1454,10 @@ EOF
test_expect_success '.gitmodules ignore=all suppresses unstaged submodule summary' '
cat > expect << EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
@@ -1472,6 +1577,10 @@ test_expect_success 'git commit --dry-run will show a staged but ignored submodu
git add sm &&
cat >expect << EOF &&
On branch master
+Your branch and '\''upstream'\'' have diverged,
+and have 2 and 2 different commits each, respectively.
+ (use "git pull" to merge the remote branch into yours)
+
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
diff --git a/t/t7810-grep.sh b/t/t7810-grep.sh
index cee42097b0..f106387820 100755
--- a/t/t7810-grep.sh
+++ b/t/t7810-grep.sh
@@ -275,12 +275,16 @@ do
test_cmp expected actual
'
- test_expect_success LIBPCRE "grep $L with grep.patterntype=perl" '
+ test_expect_success PCRE "grep $L with grep.patterntype=perl" '
echo "${HC}ab:a+b*c" >expected &&
git -c grep.patterntype=perl grep "a\x{2b}b\x{2a}c" $H ab >actual &&
test_cmp expected actual
'
+ test_expect_success !PCRE "grep $L with grep.patterntype=perl errors without PCRE" '
+ test_must_fail git -c grep.patterntype=perl grep "foo.*bar"
+ '
+
test_expect_success "grep $L with grep.patternType=default and grep.extendedRegexp=true" '
echo "${HC}ab:abc" >expected &&
git \
@@ -771,6 +775,40 @@ test_expect_success 'grep -W with userdiff' '
test_cmp expected actual
'
+for threads in $(test_seq 0 10)
+do
+ test_expect_success "grep --threads=$threads & -c grep.threads=$threads" "
+ git grep --threads=$threads . >actual.$threads &&
+ if test $threads -ge 1
+ then
+ test_cmp actual.\$(($threads - 1)) actual.$threads
+ fi &&
+ git -c grep.threads=$threads grep . >actual.$threads &&
+ if test $threads -ge 1
+ then
+ test_cmp actual.\$(($threads - 1)) actual.$threads
+ fi
+ "
+done
+
+test_expect_success !PTHREADS,C_LOCALE_OUTPUT 'grep --threads=N or pack.threads=N warns when no pthreads' '
+ git grep --threads=2 Hello hello_world 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 1 warnings &&
+ grep -F "no threads support, ignoring --threads" err &&
+ git -c grep.threads=2 grep Hello hello_world 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 1 warnings &&
+ grep -F "no threads support, ignoring grep.threads" err &&
+ git -c grep.threads=2 grep --threads=4 Hello hello_world 2>err &&
+ grep ^warning: err >warnings &&
+ test_line_count = 2 warnings &&
+ grep -F "no threads support, ignoring --threads" err &&
+ grep -F "no threads support, ignoring grep.threads" err &&
+ git -c grep.threads=0 grep --threads=0 Hello hello_world 2>err &&
+ test_line_count = 0 err
+'
+
test_expect_success 'grep from a subdirectory to search wider area (1)' '
mkdir -p s &&
(
@@ -1053,16 +1091,24 @@ hello.c:int main(int argc, const char **argv)
hello.c: printf("Hello world.\n");
EOF
-test_expect_success LIBPCRE 'grep --perl-regexp pattern' '
+test_expect_success PCRE 'grep --perl-regexp pattern' '
git grep --perl-regexp "\p{Ps}.*?\p{Pe}" hello.c >actual &&
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep -P pattern' '
+test_expect_success !PCRE 'grep --perl-regexp pattern errors without PCRE' '
+ test_must_fail git grep --perl-regexp "foo.*bar"
+'
+
+test_expect_success PCRE 'grep -P pattern' '
git grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
test_cmp expected actual
'
+test_expect_success !PCRE 'grep -P pattern errors without PCRE' '
+ test_must_fail git grep -P "foo.*bar"
+'
+
test_expect_success 'grep pattern with grep.extendedRegexp=true' '
>empty &&
test_must_fail git -c grep.extendedregexp=true \
@@ -1070,13 +1116,13 @@ test_expect_success 'grep pattern with grep.extendedRegexp=true' '
test_cmp empty actual
'
-test_expect_success LIBPCRE 'grep -P pattern with grep.extendedRegexp=true' '
+test_expect_success PCRE 'grep -P pattern with grep.extendedRegexp=true' '
git -c grep.extendedregexp=true \
grep -P "\p{Ps}.*?\p{Pe}" hello.c >actual &&
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep -P -v pattern' '
+test_expect_success PCRE 'grep -P -v pattern' '
{
echo "ab:a+b*c"
echo "ab:a+bc"
@@ -1085,7 +1131,7 @@ test_expect_success LIBPCRE 'grep -P -v pattern' '
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep -P -i pattern' '
+test_expect_success PCRE 'grep -P -i pattern' '
cat >expected <<-EOF &&
hello.c: printf("Hello world.\n");
EOF
@@ -1093,7 +1139,7 @@ test_expect_success LIBPCRE 'grep -P -i pattern' '
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep -P -w pattern' '
+test_expect_success PCRE 'grep -P -w pattern' '
{
echo "hello_world:Hello world"
echo "hello_world:HeLLo world"
@@ -1102,6 +1148,13 @@ test_expect_success LIBPCRE 'grep -P -w pattern' '
test_cmp expected actual
'
+test_expect_success PCRE 'grep -P backreferences work (the PCRE NO_AUTO_CAPTURE flag is not set)' '
+ git grep -P -h "(?P<one>.)(?P=one)" hello_world >actual &&
+ test_cmp hello_world actual &&
+ git grep -P -h "(.)\1" hello_world >actual &&
+ test_cmp hello_world actual
+'
+
test_expect_success 'grep -G invalidpattern properly dies ' '
test_must_fail git grep -G "a["
'
@@ -1118,11 +1171,11 @@ test_expect_success 'grep invalidpattern properly dies with grep.patternType=ext
test_must_fail git -c grep.patterntype=extended grep "a["
'
-test_expect_success LIBPCRE 'grep -P invalidpattern properly dies ' '
+test_expect_success PCRE 'grep -P invalidpattern properly dies ' '
test_must_fail git grep -P "a["
'
-test_expect_success LIBPCRE 'grep invalidpattern properly dies with grep.patternType=perl' '
+test_expect_success PCRE 'grep invalidpattern properly dies with grep.patternType=perl' '
test_must_fail git -c grep.patterntype=perl grep "a["
'
@@ -1191,13 +1244,13 @@ test_expect_success 'grep pattern with grep.patternType=fixed, =basic, =perl, =e
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep -G -F -E -P pattern' '
+test_expect_success PCRE 'grep -G -F -E -P pattern' '
echo "d0:0" >expected &&
git grep -G -F -E -P "[\d]" d0 >actual &&
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' '
+test_expect_success PCRE 'grep pattern with grep.patternType=fixed, =basic, =extended, =perl' '
echo "d0:0" >expected &&
git \
-c grep.patterntype=fixed \
@@ -1208,7 +1261,7 @@ test_expect_success LIBPCRE 'grep pattern with grep.patternType=fixed, =basic, =
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep -P pattern with grep.patternType=fixed' '
+test_expect_success PCRE 'grep -P pattern with grep.patternType=fixed' '
echo "ab:a+b*c" >expected &&
git \
-c grep.patterntype=fixed \
@@ -1343,12 +1396,12 @@ space: line with leading space2
space: line with leading space3
EOF
-test_expect_success LIBPCRE 'grep -E "^ "' '
+test_expect_success PCRE 'grep -E "^ "' '
git grep -E "^ " space >actual &&
test_cmp expected actual
'
-test_expect_success LIBPCRE 'grep -P "^ "' '
+test_expect_success PCRE 'grep -P "^ "' '
git grep -P "^ " space >actual &&
test_cmp expected actual
'
diff --git a/t/t7812-grep-icase-non-ascii.sh b/t/t7812-grep-icase-non-ascii.sh
index 169fd8d706..0059a1f837 100755
--- a/t/t7812-grep-icase-non-ascii.sh
+++ b/t/t7812-grep-icase-non-ascii.sh
@@ -20,13 +20,13 @@ test_expect_success REGEX_LOCALE 'grep literal string, no -F' '
git grep -i "TILRAUN: HALLÓ HEIMUR!"
'
-test_expect_success GETTEXT_LOCALE,LIBPCRE 'grep pcre utf-8 icase' '
+test_expect_success GETTEXT_LOCALE,PCRE 'grep pcre utf-8 icase' '
git grep --perl-regexp "TILRAUN: H.lló Heimur!" &&
git grep --perl-regexp -i "TILRAUN: H.lló Heimur!" &&
git grep --perl-regexp -i "TILRAUN: H.LLÓ HEIMUR!"
'
-test_expect_success GETTEXT_LOCALE,LIBPCRE 'grep pcre utf-8 string with "+"' '
+test_expect_success GETTEXT_LOCALE,PCRE 'grep pcre utf-8 string with "+"' '
test_write_lines "TILRAUN: Hallóó Heimur!" >file2 &&
git add file2 &&
git grep -l --perl-regexp "TILRAUN: H.lló+ Heimur!" >actual &&
@@ -36,29 +36,14 @@ test_expect_success GETTEXT_LOCALE,LIBPCRE 'grep pcre utf-8 string with "+"' '
'
test_expect_success REGEX_LOCALE 'grep literal string, with -F' '
- git grep --debug -i -F "TILRAUN: Halló Heimur!" 2>&1 >/dev/null |
- grep fixed >debug1 &&
- test_write_lines "fixed TILRAUN: Halló Heimur!" >expect1 &&
- test_cmp expect1 debug1 &&
-
- git grep --debug -i -F "TILRAUN: HALLÓ HEIMUR!" 2>&1 >/dev/null |
- grep fixed >debug2 &&
- test_write_lines "fixed TILRAUN: HALLÓ HEIMUR!" >expect2 &&
- test_cmp expect2 debug2
+ git grep -i -F "TILRAUN: Halló Heimur!" &&
+ git grep -i -F "TILRAUN: HALLÓ HEIMUR!"
'
test_expect_success REGEX_LOCALE 'grep string with regex, with -F' '
- test_write_lines "^*TILR^AUN:.* \\Halló \$He[]imur!\$" >file &&
-
- git grep --debug -i -F "^*TILR^AUN:.* \\Halló \$He[]imur!\$" 2>&1 >/dev/null |
- grep fixed >debug1 &&
- test_write_lines "fixed \\^*TILR^AUN:\\.\\* \\\\Halló \$He\\[]imur!\\\$" >expect1 &&
- test_cmp expect1 debug1 &&
-
- git grep --debug -i -F "^*TILR^AUN:.* \\HALLÓ \$HE[]IMUR!\$" 2>&1 >/dev/null |
- grep fixed >debug2 &&
- test_write_lines "fixed \\^*TILR^AUN:\\.\\* \\\\HALLÓ \$HE\\[]IMUR!\\\$" >expect2 &&
- test_cmp expect2 debug2
+ test_write_lines "TILRAUN: Halló Heimur [abc]!" >file3 &&
+ git add file3 &&
+ git grep -i -F "TILRAUN: Halló Heimur [abc]!" file3
'
test_expect_success REGEX_LOCALE 'pickaxe -i on non-ascii' '
diff --git a/t/t7813-grep-icase-iso.sh b/t/t7813-grep-icase-iso.sh
index efef7fb81f..701e08a8e5 100755
--- a/t/t7813-grep-icase-iso.sh
+++ b/t/t7813-grep-icase-iso.sh
@@ -11,7 +11,7 @@ test_expect_success GETTEXT_ISO_LOCALE 'setup' '
export LC_ALL
'
-test_expect_success GETTEXT_ISO_LOCALE,LIBPCRE 'grep pcre string' '
+test_expect_success GETTEXT_ISO_LOCALE,PCRE 'grep pcre string' '
git grep --perl-regexp -i "TILRAUN: H.lló Heimur!" &&
git grep --perl-regexp -i "TILRAUN: H.LLÓ HEIMUR!"
'
diff --git a/t/t7814-grep-recurse-submodules.sh b/t/t7814-grep-recurse-submodules.sh
index 5b6eb3a65e..3a58197f47 100755
--- a/t/t7814-grep-recurse-submodules.sh
+++ b/t/t7814-grep-recurse-submodules.sh
@@ -9,13 +9,13 @@ submodules.
. ./test-lib.sh
test_expect_success 'setup directory structure and submodule' '
- echo "foobar" >a &&
+ echo "(1|2)d(3|4)" >a &&
mkdir b &&
- echo "bar" >b/b &&
+ echo "(3|4)" >b/b &&
git add a b &&
git commit -m "add a and b" &&
git init submodule &&
- echo "foobar" >submodule/a &&
+ echo "(1|2)d(3|4)" >submodule/a &&
git -C submodule add a &&
git -C submodule commit -m "add a" &&
git submodule add ./submodule &&
@@ -24,18 +24,18 @@ test_expect_success 'setup directory structure and submodule' '
test_expect_success 'grep correctly finds patterns in a submodule' '
cat >expect <<-\EOF &&
- a:foobar
- b/b:bar
- submodule/a:foobar
+ a:(1|2)d(3|4)
+ b/b:(3|4)
+ submodule/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules >actual &&
+ git grep -e "(3|4)" --recurse-submodules >actual &&
test_cmp expect actual
'
test_expect_success 'grep and basic pathspecs' '
cat >expect <<-\EOF &&
- submodule/a:foobar
+ submodule/a:(1|2)d(3|4)
EOF
git grep -e. --recurse-submodules -- submodule >actual &&
@@ -44,7 +44,7 @@ test_expect_success 'grep and basic pathspecs' '
test_expect_success 'grep and nested submodules' '
git init submodule/sub &&
- echo "foobar" >submodule/sub/a &&
+ echo "(1|2)d(3|4)" >submodule/sub/a &&
git -C submodule/sub add a &&
git -C submodule/sub commit -m "add a" &&
git -C submodule submodule add ./sub &&
@@ -54,117 +54,117 @@ test_expect_success 'grep and nested submodules' '
git commit -m "updated submodule" &&
cat >expect <<-\EOF &&
- a:foobar
- b/b:bar
- submodule/a:foobar
- submodule/sub/a:foobar
+ a:(1|2)d(3|4)
+ b/b:(3|4)
+ submodule/a:(1|2)d(3|4)
+ submodule/sub/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules >actual &&
+ git grep -e "(3|4)" --recurse-submodules >actual &&
test_cmp expect actual
'
test_expect_success 'grep and multiple patterns' '
cat >expect <<-\EOF &&
- a:foobar
- submodule/a:foobar
- submodule/sub/a:foobar
+ a:(1|2)d(3|4)
+ submodule/a:(1|2)d(3|4)
+ submodule/sub/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --and -e "foo" --recurse-submodules >actual &&
+ git grep -e "(3|4)" --and -e "(1|2)" --recurse-submodules >actual &&
test_cmp expect actual
'
test_expect_success 'grep and multiple patterns' '
cat >expect <<-\EOF &&
- b/b:bar
+ b/b:(3|4)
EOF
- git grep -e "bar" --and --not -e "foo" --recurse-submodules >actual &&
+ git grep -e "(3|4)" --and --not -e "(1|2)" --recurse-submodules >actual &&
test_cmp expect actual
'
test_expect_success 'basic grep tree' '
cat >expect <<-\EOF &&
- HEAD:a:foobar
- HEAD:b/b:bar
- HEAD:submodule/a:foobar
- HEAD:submodule/sub/a:foobar
+ HEAD:a:(1|2)d(3|4)
+ HEAD:b/b:(3|4)
+ HEAD:submodule/a:(1|2)d(3|4)
+ HEAD:submodule/sub/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules HEAD >actual &&
+ git grep -e "(3|4)" --recurse-submodules HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'grep tree HEAD^' '
cat >expect <<-\EOF &&
- HEAD^:a:foobar
- HEAD^:b/b:bar
- HEAD^:submodule/a:foobar
+ HEAD^:a:(1|2)d(3|4)
+ HEAD^:b/b:(3|4)
+ HEAD^:submodule/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules HEAD^ >actual &&
+ git grep -e "(3|4)" --recurse-submodules HEAD^ >actual &&
test_cmp expect actual
'
test_expect_success 'grep tree HEAD^^' '
cat >expect <<-\EOF &&
- HEAD^^:a:foobar
- HEAD^^:b/b:bar
+ HEAD^^:a:(1|2)d(3|4)
+ HEAD^^:b/b:(3|4)
EOF
- git grep -e "bar" --recurse-submodules HEAD^^ >actual &&
+ git grep -e "(3|4)" --recurse-submodules HEAD^^ >actual &&
test_cmp expect actual
'
test_expect_success 'grep tree and pathspecs' '
cat >expect <<-\EOF &&
- HEAD:submodule/a:foobar
- HEAD:submodule/sub/a:foobar
+ HEAD:submodule/a:(1|2)d(3|4)
+ HEAD:submodule/sub/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules HEAD -- submodule >actual &&
+ git grep -e "(3|4)" --recurse-submodules HEAD -- submodule >actual &&
test_cmp expect actual
'
test_expect_success 'grep tree and pathspecs' '
cat >expect <<-\EOF &&
- HEAD:submodule/a:foobar
- HEAD:submodule/sub/a:foobar
+ HEAD:submodule/a:(1|2)d(3|4)
+ HEAD:submodule/sub/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules HEAD -- "submodule*a" >actual &&
+ git grep -e "(3|4)" --recurse-submodules HEAD -- "submodule*a" >actual &&
test_cmp expect actual
'
test_expect_success 'grep tree and more pathspecs' '
cat >expect <<-\EOF &&
- HEAD:submodule/a:foobar
+ HEAD:submodule/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules HEAD -- "submodul?/a" >actual &&
+ git grep -e "(3|4)" --recurse-submodules HEAD -- "submodul?/a" >actual &&
test_cmp expect actual
'
test_expect_success 'grep tree and more pathspecs' '
cat >expect <<-\EOF &&
- HEAD:submodule/sub/a:foobar
+ HEAD:submodule/sub/a:(1|2)d(3|4)
EOF
- git grep -e "bar" --recurse-submodules HEAD -- "submodul*/sub/a" >actual &&
+ git grep -e "(3|4)" --recurse-submodules HEAD -- "submodul*/sub/a" >actual &&
test_cmp expect actual
'
test_expect_success !MINGW 'grep recurse submodule colon in name' '
git init parent &&
test_when_finished "rm -rf parent" &&
- echo "foobar" >"parent/fi:le" &&
+ echo "(1|2)d(3|4)" >"parent/fi:le" &&
git -C parent add "fi:le" &&
git -C parent commit -m "add fi:le" &&
git init "su:b" &&
test_when_finished "rm -rf su:b" &&
- echo "foobar" >"su:b/fi:le" &&
+ echo "(1|2)d(3|4)" >"su:b/fi:le" &&
git -C "su:b" add "fi:le" &&
git -C "su:b" commit -m "add fi:le" &&
@@ -172,30 +172,30 @@ test_expect_success !MINGW 'grep recurse submodule colon in name' '
git -C parent commit -m "add submodule" &&
cat >expect <<-\EOF &&
- fi:le:foobar
- su:b/fi:le:foobar
+ fi:le:(1|2)d(3|4)
+ su:b/fi:le:(1|2)d(3|4)
EOF
- git -C parent grep -e "foobar" --recurse-submodules >actual &&
+ git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual &&
test_cmp expect actual &&
cat >expect <<-\EOF &&
- HEAD:fi:le:foobar
- HEAD:su:b/fi:le:foobar
+ HEAD:fi:le:(1|2)d(3|4)
+ HEAD:su:b/fi:le:(1|2)d(3|4)
EOF
- git -C parent grep -e "foobar" --recurse-submodules HEAD >actual &&
+ git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules HEAD >actual &&
test_cmp expect actual
'
test_expect_success 'grep history with moved submoules' '
git init parent &&
test_when_finished "rm -rf parent" &&
- echo "foobar" >parent/file &&
+ echo "(1|2)d(3|4)" >parent/file &&
git -C parent add file &&
git -C parent commit -m "add file" &&
git init sub &&
test_when_finished "rm -rf sub" &&
- echo "foobar" >sub/file &&
+ echo "(1|2)d(3|4)" >sub/file &&
git -C sub add file &&
git -C sub commit -m "add file" &&
@@ -203,82 +203,82 @@ test_expect_success 'grep history with moved submoules' '
git -C parent commit -m "add submodule" &&
cat >expect <<-\EOF &&
- dir/sub/file:foobar
- file:foobar
+ dir/sub/file:(1|2)d(3|4)
+ file:(1|2)d(3|4)
EOF
- git -C parent grep -e "foobar" --recurse-submodules >actual &&
+ git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual &&
test_cmp expect actual &&
git -C parent mv dir/sub sub-moved &&
git -C parent commit -m "moved submodule" &&
cat >expect <<-\EOF &&
- file:foobar
- sub-moved/file:foobar
+ file:(1|2)d(3|4)
+ sub-moved/file:(1|2)d(3|4)
EOF
- git -C parent grep -e "foobar" --recurse-submodules >actual &&
+ git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules >actual &&
test_cmp expect actual &&
cat >expect <<-\EOF &&
- HEAD^:dir/sub/file:foobar
- HEAD^:file:foobar
+ HEAD^:dir/sub/file:(1|2)d(3|4)
+ HEAD^:file:(1|2)d(3|4)
EOF
- git -C parent grep -e "foobar" --recurse-submodules HEAD^ >actual &&
+ git -C parent grep -e "(1|2)d(3|4)" --recurse-submodules HEAD^ >actual &&
test_cmp expect actual
'
test_expect_success 'grep using relative path' '
test_when_finished "rm -rf parent sub" &&
git init sub &&
- echo "foobar" >sub/file &&
+ echo "(1|2)d(3|4)" >sub/file &&
git -C sub add file &&
git -C sub commit -m "add file" &&
git init parent &&
- echo "foobar" >parent/file &&
+ echo "(1|2)d(3|4)" >parent/file &&
git -C parent add file &&
mkdir parent/src &&
- echo "foobar" >parent/src/file2 &&
+ echo "(1|2)d(3|4)" >parent/src/file2 &&
git -C parent add src/file2 &&
git -C parent submodule add ../sub &&
git -C parent commit -m "add files and submodule" &&
# From top works
cat >expect <<-\EOF &&
- file:foobar
- src/file2:foobar
- sub/file:foobar
+ file:(1|2)d(3|4)
+ src/file2:(1|2)d(3|4)
+ sub/file:(1|2)d(3|4)
EOF
- git -C parent grep --recurse-submodules -e "foobar" >actual &&
+ git -C parent grep --recurse-submodules -e "(1|2)d(3|4)" >actual &&
test_cmp expect actual &&
# Relative path to top
cat >expect <<-\EOF &&
- ../file:foobar
- file2:foobar
- ../sub/file:foobar
+ ../file:(1|2)d(3|4)
+ file2:(1|2)d(3|4)
+ ../sub/file:(1|2)d(3|4)
EOF
- git -C parent/src grep --recurse-submodules -e "foobar" -- .. >actual &&
+ git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" -- .. >actual &&
test_cmp expect actual &&
# Relative path to submodule
cat >expect <<-\EOF &&
- ../sub/file:foobar
+ ../sub/file:(1|2)d(3|4)
EOF
- git -C parent/src grep --recurse-submodules -e "foobar" -- ../sub >actual &&
+ git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" -- ../sub >actual &&
test_cmp expect actual
'
test_expect_success 'grep from a subdir' '
test_when_finished "rm -rf parent sub" &&
git init sub &&
- echo "foobar" >sub/file &&
+ echo "(1|2)d(3|4)" >sub/file &&
git -C sub add file &&
git -C sub commit -m "add file" &&
git init parent &&
mkdir parent/src &&
- echo "foobar" >parent/src/file &&
+ echo "(1|2)d(3|4)" >parent/src/file &&
git -C parent add src/file &&
git -C parent submodule add ../sub src/sub &&
git -C parent submodule add ../sub sub &&
@@ -286,19 +286,19 @@ test_expect_success 'grep from a subdir' '
# Verify grep from root works
cat >expect <<-\EOF &&
- src/file:foobar
- src/sub/file:foobar
- sub/file:foobar
+ src/file:(1|2)d(3|4)
+ src/sub/file:(1|2)d(3|4)
+ sub/file:(1|2)d(3|4)
EOF
- git -C parent grep --recurse-submodules -e "foobar" >actual &&
+ git -C parent grep --recurse-submodules -e "(1|2)d(3|4)" >actual &&
test_cmp expect actual &&
# Verify grep from a subdir works
cat >expect <<-\EOF &&
- file:foobar
- sub/file:foobar
+ file:(1|2)d(3|4)
+ sub/file:(1|2)d(3|4)
EOF
- git -C parent/src grep --recurse-submodules -e "foobar" >actual &&
+ git -C parent/src grep --recurse-submodules -e "(1|2)d(3|4)" >actual &&
test_cmp expect actual
'
@@ -313,4 +313,53 @@ test_incompatible_with_recurse_submodules ()
test_incompatible_with_recurse_submodules --untracked
test_incompatible_with_recurse_submodules --no-index
+test_expect_success 'grep --recurse-submodules should pass the pattern type along' '
+ # Fixed
+ test_must_fail git grep -F --recurse-submodules -e "(.|.)[\d]" &&
+ test_must_fail git -c grep.patternType=fixed grep --recurse-submodules -e "(.|.)[\d]" &&
+
+ # Basic
+ git grep -G --recurse-submodules -e "(.|.)[\d]" >actual &&
+ cat >expect <<-\EOF &&
+ a:(1|2)d(3|4)
+ submodule/a:(1|2)d(3|4)
+ submodule/sub/a:(1|2)d(3|4)
+ EOF
+ test_cmp expect actual &&
+ git -c grep.patternType=basic grep --recurse-submodules -e "(.|.)[\d]" >actual &&
+ test_cmp expect actual &&
+
+ # Extended
+ git grep -E --recurse-submodules -e "(.|.)[\d]" >actual &&
+ cat >expect <<-\EOF &&
+ .gitmodules:[submodule "submodule"]
+ .gitmodules: path = submodule
+ .gitmodules: url = ./submodule
+ a:(1|2)d(3|4)
+ submodule/.gitmodules:[submodule "sub"]
+ submodule/a:(1|2)d(3|4)
+ submodule/sub/a:(1|2)d(3|4)
+ EOF
+ test_cmp expect actual &&
+ git -c grep.patternType=extended grep --recurse-submodules -e "(.|.)[\d]" >actual &&
+ test_cmp expect actual &&
+ git -c grep.extendedRegexp=true grep --recurse-submodules -e "(.|.)[\d]" >actual &&
+ test_cmp expect actual &&
+
+ # Perl
+ if test_have_prereq PCRE
+ then
+ git grep -P --recurse-submodules -e "(.|.)[\d]" >actual &&
+ cat >expect <<-\EOF &&
+ a:(1|2)d(3|4)
+ b/b:(3|4)
+ submodule/a:(1|2)d(3|4)
+ submodule/sub/a:(1|2)d(3|4)
+ EOF
+ test_cmp expect actual &&
+ git -c grep.patternType=perl grep --recurse-submodules -e "(.|.)[\d]" >actual &&
+ test_cmp expect actual
+ fi
+'
+
test_done
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 60a80f60b2..d1e4e8ad19 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -1913,4 +1913,52 @@ test_expect_success $PREREQ 'leading and trailing whitespaces are removed' '
test_cmp expected-list actual-list
'
+test_expect_success $PREREQ 'invoke hook' '
+ mkdir -p .git/hooks &&
+
+ write_script .git/hooks/sendemail-validate <<-\EOF &&
+ # test that we have the correct environment variable, pwd, and
+ # argument
+ case "$GIT_DIR" in
+ *.git)
+ true
+ ;;
+ *)
+ false
+ ;;
+ esac &&
+ test -f 0001-add-master.patch &&
+ grep "add master" "$1"
+ EOF
+
+ mkdir subdir &&
+ (
+ # Test that it works even if we are not at the root of the
+ # working tree
+ cd subdir &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/../fake.sendmail" \
+ ../0001-add-master.patch &&
+
+ # Verify error message when a patch is rejected by the hook
+ sed -e "s/add master/x/" ../0001-add-master.patch >../another.patch &&
+ git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/../fake.sendmail" \
+ ../another.patch 2>err
+ test_i18ngrep "rejected by sendemail-validate hook" err
+ )
+'
+
+test_expect_success $PREREQ 'test that send-email works outside a repo' '
+ nongit git send-email \
+ --from="Example <nobody@example.com>" \
+ --to=nobody@example.com \
+ --smtp-server="$(pwd)/fake.sendmail" \
+ "$(pwd)/0001-add-master.patch"
+'
+
test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 13b5696822..4936725c67 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -745,26 +745,36 @@ test_done () {
fi
case "$test_failure" in
0)
- # Maybe print SKIP message
- if test -n "$skip_all" && test $test_count -gt 0
- then
- error "Can't use skip_all after running some tests"
- fi
- test -z "$skip_all" || skip_all=" # SKIP $skip_all"
-
if test $test_external_has_tap -eq 0
then
if test $test_remaining -gt 0
then
say_color pass "# passed all $msg"
fi
- say "1..$test_count$skip_all"
+
+ # Maybe print SKIP message
+ test -z "$skip_all" || skip_all="# SKIP $skip_all"
+ case "$test_count" in
+ 0)
+ say "1..$test_count${skip_all:+ $skip_all}"
+ ;;
+ *)
+ test -z "$skip_all" ||
+ say_color warn "$skip_all"
+ say "1..$test_count"
+ ;;
+ esac
fi
- test -d "$remove_trash" &&
- cd "$(dirname "$remove_trash")" &&
- rm -rf "$(basename "$remove_trash")"
+ if test -z "$debug"
+ then
+ test -d "$TRASH_DIRECTORY" ||
+ error "Tests passed but trash directory already removed before test cleanup; aborting"
+ cd "$TRASH_DIRECTORY/.." &&
+ rm -fr "$TRASH_DIRECTORY" ||
+ error "Tests passed but test cleanup failed; aborting"
+ fi
test_at_end_hook_
exit 0 ;;
@@ -919,7 +929,6 @@ case "$TRASH_DIRECTORY" in
/*) ;; # absolute path is good
*) TRASH_DIRECTORY="$TEST_OUTPUT_DIRECTORY/$TRASH_DIRECTORY" ;;
esac
-test ! -z "$debug" || remove_trash=$TRASH_DIRECTORY
rm -fr "$TRASH_DIRECTORY" || {
GIT_EXIT_OK=t
echo >&5 "FATAL: Cannot prepare test area"
@@ -1009,8 +1018,9 @@ esac
( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
test -z "$NO_PERL" && test_set_prereq PERL
+test -z "$NO_PTHREADS" && test_set_prereq PTHREADS
test -z "$NO_PYTHON" && test_set_prereq PYTHON
-test -n "$USE_LIBPCRE" && test_set_prereq LIBPCRE
+test -n "$USE_LIBPCRE1" && test_set_prereq PCRE
test -z "$NO_GETTEXT" && test_set_prereq GETTEXT
# Can we rely on git's output in the C locale?
@@ -1164,3 +1174,6 @@ build_option () {
test_lazy_prereq LONG_IS_64BIT '
test 8 -le "$(build_option sizeof-long)"
'
+
+test_lazy_prereq TIME_IS_64BIT 'test-date is64bit'
+test_lazy_prereq TIME_T_IS_64BIT 'test-date time_t-is64bit'
diff --git a/tag.c b/tag.c
index 243d1fdbbc..47f60ae151 100644
--- a/tag.c
+++ b/tag.c
@@ -66,7 +66,7 @@ struct object *deref_tag(struct object *o, const char *warn, int warnlen)
{
while (o && o->type == OBJ_TAG)
if (((struct tag *)o)->tagged)
- o = parse_object(((struct tag *)o)->tagged->oid.hash);
+ o = parse_object(&((struct tag *)o)->tagged->oid);
else
o = NULL;
if (!o && warn) {
@@ -80,7 +80,7 @@ struct object *deref_tag(struct object *o, const char *warn, int warnlen)
struct object *deref_tag_noverify(struct object *o)
{
while (o && o->type == OBJ_TAG) {
- o = parse_object(o->oid.hash);
+ o = parse_object(&o->oid);
if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
o = ((struct tag *)o)->tagged;
else
@@ -89,15 +89,15 @@ struct object *deref_tag_noverify(struct object *o)
return o;
}
-struct tag *lookup_tag(const unsigned char *sha1)
+struct tag *lookup_tag(const struct object_id *oid)
{
- struct object *obj = lookup_object(sha1);
+ struct object *obj = lookup_object(oid->hash);
if (!obj)
- return create_object(sha1, alloc_tag_node());
+ return create_object(oid->hash, alloc_tag_node());
return object_as_type(obj, OBJ_TAG, 0);
}
-static unsigned long parse_tag_date(const char *buf, const char *tail)
+static timestamp_t parse_tag_date(const char *buf, const char *tail)
{
const char *dateptr;
@@ -110,13 +110,13 @@ static unsigned long parse_tag_date(const char *buf, const char *tail)
/* nada */;
if (buf >= tail)
return 0;
- /* dateptr < buf && buf[-1] == '\n', so strtoul will stop at buf-1 */
- return strtoul(dateptr, NULL, 10);
+ /* dateptr < buf && buf[-1] == '\n', so parsing will stop at buf-1 */
+ return parse_timestamp(dateptr, NULL, 10);
}
int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
{
- unsigned char sha1[20];
+ struct object_id oid;
char type[20];
const char *bufptr = data;
const char *tail = bufptr + size;
@@ -126,11 +126,10 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
return 0;
item->object.parsed = 1;
- if (size < 64)
+ if (size < GIT_SHA1_HEXSZ + 24)
return -1;
- if (memcmp("object ", bufptr, 7) || get_sha1_hex(bufptr + 7, sha1) || bufptr[47] != '\n')
+ if (memcmp("object ", bufptr, 7) || parse_oid_hex(bufptr + 7, &oid, &bufptr) || *bufptr++ != '\n')
return -1;
- bufptr += 48; /* "object " + sha1 + "\n" */
if (!starts_with(bufptr, "type "))
return -1;
@@ -143,13 +142,13 @@ int parse_tag_buffer(struct tag *item, const void *data, unsigned long size)
bufptr = nl + 1;
if (!strcmp(type, blob_type)) {
- item->tagged = &lookup_blob(sha1)->object;
+ item->tagged = &lookup_blob(&oid)->object;
} else if (!strcmp(type, tree_type)) {
- item->tagged = &lookup_tree(sha1)->object;
+ item->tagged = &lookup_tree(&oid)->object;
} else if (!strcmp(type, commit_type)) {
- item->tagged = &lookup_commit(sha1)->object;
+ item->tagged = &lookup_commit(&oid)->object;
} else if (!strcmp(type, tag_type)) {
- item->tagged = &lookup_tag(sha1)->object;
+ item->tagged = &lookup_tag(&oid)->object;
} else {
error("Unknown type %s", type);
item->tagged = NULL;
diff --git a/tag.h b/tag.h
index a5721b6731..fdfcb4a84a 100644
--- a/tag.h
+++ b/tag.h
@@ -9,10 +9,10 @@ struct tag {
struct object object;
struct object *tagged;
char *tag;
- unsigned long date;
+ timestamp_t date;
};
-extern struct tag *lookup_tag(const unsigned char *sha1);
+extern struct tag *lookup_tag(const struct object_id *oid);
extern int parse_tag_buffer(struct tag *item, const void *data, unsigned long size);
extern int parse_tag(struct tag *item);
extern struct object *deref_tag(struct object *, const char *, int);
diff --git a/transport.c b/transport.c
index 4d33138a75..9bfcf870f9 100644
--- a/transport.c
+++ b/transport.c
@@ -87,7 +87,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport, int for_pus
for (i = 0; i < data->header.references.nr; i++) {
struct ref_list_entry *e = data->header.references.list + i;
struct ref *ref = alloc_ref(e->name);
- hashcpy(ref->old_oid.hash, e->sha1);
+ oidcpy(&ref->old_oid, &e->oid);
ref->next = result;
result = ref;
}
diff --git a/tree-walk.c b/tree-walk.c
index f25a08fddf..6a42e402b0 100644
--- a/tree-walk.c
+++ b/tree-walk.c
@@ -589,7 +589,6 @@ enum follow_symlinks_result get_tree_entry_follow_symlinks(unsigned char *tree_s
int i;
init_tree_desc(&t, NULL, 0UL);
- strbuf_init(result_path, 0);
strbuf_addstr(&namebuf, name);
hashcpy(current_tree_sha1, tree_sha1);
diff --git a/tree.c b/tree.c
index ce345c5511..603b29ee80 100644
--- a/tree.c
+++ b/tree.c
@@ -58,7 +58,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
{
struct tree_desc desc;
struct name_entry entry;
- unsigned char sha1[20];
+ struct object_id oid;
int len, oldlen = base->len;
enum interesting retval = entry_not_interesting;
@@ -87,11 +87,11 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
}
if (S_ISDIR(entry.mode))
- hashcpy(sha1, entry.oid->hash);
+ oidcpy(&oid, entry.oid);
else if (S_ISGITLINK(entry.mode)) {
struct commit *commit;
- commit = lookup_commit(entry.oid->hash);
+ commit = lookup_commit(entry.oid);
if (!commit)
die("Commit %s in submodule path %s%s not found",
oid_to_hex(entry.oid),
@@ -102,7 +102,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
oid_to_hex(entry.oid),
base->buf, entry.path);
- hashcpy(sha1, commit->tree->object.oid.hash);
+ oidcpy(&oid, &commit->tree->object.oid);
}
else
continue;
@@ -110,7 +110,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
len = tree_entry_len(&entry);
strbuf_add(base, entry.path, len);
strbuf_addch(base, '/');
- retval = read_tree_1(lookup_tree(sha1),
+ retval = read_tree_1(lookup_tree(&oid),
base, stage, pathspec,
fn, context);
strbuf_setlen(base, oldlen);
@@ -184,11 +184,11 @@ int read_tree(struct tree *tree, int stage, struct pathspec *match)
return 0;
}
-struct tree *lookup_tree(const unsigned char *sha1)
+struct tree *lookup_tree(const struct object_id *oid)
{
- struct object *obj = lookup_object(sha1);
+ struct object *obj = lookup_object(oid->hash);
if (!obj)
- return create_object(sha1, alloc_tree_node());
+ return create_object(oid->hash, alloc_tree_node());
return object_as_type(obj, OBJ_TREE, 0);
}
@@ -232,9 +232,9 @@ void free_tree_buffer(struct tree *tree)
tree->object.parsed = 0;
}
-struct tree *parse_tree_indirect(const unsigned char *sha1)
+struct tree *parse_tree_indirect(const struct object_id *oid)
{
- struct object *obj = parse_object(sha1);
+ struct object *obj = parse_object(oid);
do {
if (!obj)
return NULL;
@@ -247,6 +247,6 @@ struct tree *parse_tree_indirect(const unsigned char *sha1)
else
return NULL;
if (!obj->parsed)
- parse_object(obj->oid.hash);
+ parse_object(&obj->oid);
} while (1);
}
diff --git a/tree.h b/tree.h
index d24786cba2..0d4734b94b 100644
--- a/tree.h
+++ b/tree.h
@@ -12,7 +12,7 @@ struct tree {
unsigned long size;
};
-struct tree *lookup_tree(const unsigned char *sha1);
+struct tree *lookup_tree(const struct object_id *oid);
int parse_tree_buffer(struct tree *item, void *buffer, unsigned long size);
@@ -24,7 +24,7 @@ static inline int parse_tree(struct tree *tree)
void free_tree_buffer(struct tree *tree);
/* Parses and returns the tree in the given ent, chasing tags and commits. */
-struct tree *parse_tree_indirect(const unsigned char *sha1);
+struct tree *parse_tree_indirect(const struct object_id *oid);
#define READ_TREE_RECURSIVE 1
typedef int (*read_tree_fn_t)(const unsigned char *, struct strbuf *, const char *, unsigned int, int, void *);
diff --git a/unpack-trees.c b/unpack-trees.c
index aa15111fef..d38c37e38c 100644
--- a/unpack-trees.c
+++ b/unpack-trees.c
@@ -252,14 +252,18 @@ static int check_submodule_move_head(const struct cache_entry *ce,
const char *new_id,
struct unpack_trees_options *o)
{
+ unsigned flags = SUBMODULE_MOVE_HEAD_DRY_RUN;
const struct submodule *sub = submodule_from_ce(ce);
if (!sub)
return 0;
+ if (o->reset)
+ flags |= SUBMODULE_MOVE_HEAD_FORCE;
+
switch (sub->update_strategy.type) {
case SM_UPDATE_UNSPECIFIED:
case SM_UPDATE_CHECKOUT:
- if (submodule_move_head(ce->name, old_id, new_id, SUBMODULE_MOVE_HEAD_DRY_RUN))
+ if (submodule_move_head(ce->name, old_id, new_id, flags))
return o->gently ? -1 :
add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
return 0;
@@ -308,6 +312,7 @@ static void unlink_entry(const struct cache_entry *ce)
case SM_UPDATE_CHECKOUT:
case SM_UPDATE_REBASE:
case SM_UPDATE_MERGE:
+ /* state.force is set at the caller. */
submodule_move_head(ce->name, "HEAD", NULL,
SUBMODULE_MOVE_HEAD_FORCE);
break;
@@ -1068,7 +1073,7 @@ static int clear_ce_flags_dir(struct cache_entry **cache, int nr,
struct cache_entry **cache_end;
int dtype = DT_DIR;
int ret = is_excluded_from_list(prefix->buf, prefix->len,
- basename, &dtype, el);
+ basename, &dtype, el, &the_index);
int rc;
strbuf_addch(prefix, '/');
@@ -1171,7 +1176,7 @@ static int clear_ce_flags_1(struct cache_entry **cache, int nr,
/* Non-directory */
dtype = ce_to_dtype(ce);
ret = is_excluded_from_list(ce->name, ce_namelen(ce),
- name, &dtype, el);
+ name, &dtype, el, &the_index);
if (ret < 0)
ret = defval;
if (ret > 0)
@@ -1251,7 +1256,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
o->skip_sparse_checkout = 1;
if (!o->skip_sparse_checkout) {
char *sparse = git_pathdup("info/sparse-checkout");
- if (add_excludes_from_file_to_list(sparse, "", 0, &el, 0) < 0)
+ if (add_excludes_from_file_to_list(sparse, "", 0, &el, NULL) < 0)
o->skip_sparse_checkout = 1;
else
o->el = &el;
@@ -1391,6 +1396,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options
WRITE_TREE_SILENT |
WRITE_TREE_REPAIR);
}
+ move_index_extensions(&o->result, o->dst_index);
discard_index(o->dst_index);
*o->dst_index = o->result;
} else {
@@ -1592,7 +1598,7 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
memset(&d, 0, sizeof(d));
if (o->dir)
d.exclude_per_dir = o->dir->exclude_per_dir;
- i = read_directory(&d, pathbuf, namelen+1, NULL);
+ i = read_directory(&d, &the_index, pathbuf, namelen+1, NULL);
if (i)
return o->gently ? -1 :
add_rejected_path(o, ERROR_NOT_UPTODATE_DIR, ce->name);
@@ -1634,7 +1640,7 @@ static int check_ok_to_remove(const char *name, int len, int dtype,
return 0;
if (o->dir &&
- is_excluded(o->dir, name, &dtype))
+ is_excluded(o->dir, &the_index, name, &dtype))
/*
* ce->name is explicitly excluded, so it is Ok to
* overwrite it.
diff --git a/upload-pack.c b/upload-pack.c
index ffb028d623..5330c02c14 100644
--- a/upload-pack.c
+++ b/upload-pack.c
@@ -35,7 +35,7 @@ static const char * const upload_pack_usage[] = {
#define CLIENT_SHALLOW (1u << 18)
#define HIDDEN_REF (1u << 19)
-static unsigned long oldest_have;
+static timestamp_t oldest_have;
static int deepen_relative;
static int multi_ack;
@@ -286,19 +286,19 @@ static void create_pack_file(void)
die("git upload-pack: %s", abort_msg);
}
-static int got_sha1(const char *hex, unsigned char *sha1)
+static int got_oid(const char *hex, struct object_id *oid)
{
struct object *o;
int we_knew_they_have = 0;
- if (get_sha1_hex(hex, sha1))
+ if (get_oid_hex(hex, oid))
die("git upload-pack: expected SHA1 object, got '%s'", hex);
- if (!has_sha1_file(sha1))
+ if (!has_object_file(oid))
return -1;
- o = parse_object(sha1);
+ o = parse_object(oid);
if (!o)
- die("oops (%s)", sha1_to_hex(sha1));
+ die("oops (%s)", oid_to_hex(oid));
if (o->type == OBJ_COMMIT) {
struct commit_list *parents;
struct commit *commit = (struct commit *)o;
@@ -334,7 +334,7 @@ static int reachable(struct commit *want)
break;
}
if (!commit->object.parsed)
- parse_object(commit->object.oid.hash);
+ parse_object(&commit->object.oid);
if (commit->object.flags & REACHABLE)
continue;
commit->object.flags |= REACHABLE;
@@ -382,8 +382,8 @@ static int ok_to_give_up(void)
static int get_common_commits(void)
{
- unsigned char sha1[20];
- char last_hex[41];
+ struct object_id oid;
+ char last_hex[GIT_MAX_HEXSZ + 1];
int got_common = 0;
int got_other = 0;
int sent_ready = 0;
@@ -416,11 +416,11 @@ static int get_common_commits(void)
continue;
}
if (skip_prefix(line, "have ", &arg)) {
- switch (got_sha1(arg, sha1)) {
+ switch (got_oid(arg, &oid)) {
case -1: /* they have what we do not */
got_other = 1;
if (multi_ack && ok_to_give_up()) {
- const char *hex = sha1_to_hex(sha1);
+ const char *hex = oid_to_hex(&oid);
if (multi_ack == 2) {
sent_ready = 1;
packet_write_fmt(1, "ACK %s ready\n", hex);
@@ -430,7 +430,7 @@ static int get_common_commits(void)
break;
default:
got_common = 1;
- memcpy(last_hex, sha1_to_hex(sha1), 41);
+ memcpy(last_hex, oid_to_hex(&oid), 41);
if (multi_ack == 2)
packet_write_fmt(1, "ACK %s common\n", last_hex);
else if (multi_ack)
@@ -492,7 +492,7 @@ static int do_reachable_revlist(struct child_process *cmd,
goto error;
namebuf[0] = '^';
- namebuf[41] = '\n';
+ namebuf[GIT_SHA1_HEXSZ + 1] = '\n';
for (i = get_max_object_index(); 0 < i; ) {
o = get_indexed_object(--i);
if (!o)
@@ -502,10 +502,10 @@ static int do_reachable_revlist(struct child_process *cmd,
if (!is_our_ref(o))
continue;
memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
- if (write_in_full(cmd->in, namebuf, 42) < 0)
+ if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 2) < 0)
goto error;
}
- namebuf[40] = '\n';
+ namebuf[GIT_SHA1_HEXSZ] = '\n';
for (i = 0; i < src->nr; i++) {
o = src->objects[i].item;
if (is_our_ref(o)) {
@@ -516,7 +516,7 @@ static int do_reachable_revlist(struct child_process *cmd,
if (reachable && o->type == OBJ_COMMIT)
o->flags |= TMP_MARK;
memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
- if (write_in_full(cmd->in, namebuf, 41) < 0)
+ if (write_in_full(cmd->in, namebuf, GIT_SHA1_HEXSZ + 1) < 0)
goto error;
}
close(cmd->in);
@@ -642,7 +642,7 @@ static void send_shallow(struct commit_list *result)
if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
packet_write_fmt(1, "shallow %s",
oid_to_hex(&object->oid));
- register_shallow(object->oid.hash);
+ register_shallow(&object->oid);
shallow_nr++;
}
result = result->next;
@@ -667,7 +667,7 @@ static void send_unshallow(const struct object_array *shallows)
* parse and add the parents to the want list, then
* re-register it.
*/
- unregister_shallow(object->oid.hash);
+ unregister_shallow(&object->oid);
object->parsed = 0;
parse_commit_or_die((struct commit *)object);
parents = ((struct commit *)object)->parents;
@@ -679,7 +679,7 @@ static void send_unshallow(const struct object_array *shallows)
add_object_array(object, NULL, &extra_edge_obj);
}
/* make sure commit traversal conforms to client */
- register_shallow(object->oid.hash);
+ register_shallow(&object->oid);
}
}
@@ -735,14 +735,14 @@ static void receive_needs(void)
struct string_list deepen_not = STRING_LIST_INIT_DUP;
int depth = 0;
int has_non_tip = 0;
- unsigned long deepen_since = 0;
+ timestamp_t deepen_since = 0;
int deepen_rev_list = 0;
shallow_nr = 0;
for (;;) {
struct object *o;
const char *features;
- unsigned char sha1_buf[20];
+ struct object_id oid_buf;
char *line = packet_read_line(0, NULL);
const char *arg;
@@ -751,15 +751,15 @@ static void receive_needs(void)
break;
if (skip_prefix(line, "shallow ", &arg)) {
- unsigned char sha1[20];
+ struct object_id oid;
struct object *object;
- if (get_sha1_hex(arg, sha1))
+ if (get_oid_hex(arg, &oid))
die("invalid shallow line: %s", line);
- object = parse_object(sha1);
+ object = parse_object(&oid);
if (!object)
continue;
if (object->type != OBJ_COMMIT)
- die("invalid shallow object %s", sha1_to_hex(sha1));
+ die("invalid shallow object %s", oid_to_hex(&oid));
if (!(object->flags & CLIENT_SHALLOW)) {
object->flags |= CLIENT_SHALLOW;
add_object_array(object, NULL, &shallows);
@@ -775,7 +775,7 @@ static void receive_needs(void)
}
if (skip_prefix(line, "deepen-since ", &arg)) {
char *end = NULL;
- deepen_since = strtoul(arg, &end, 0);
+ deepen_since = parse_timestamp(arg, &end, 0);
if (!end || *end || !deepen_since ||
/* revisions.c's max_age -1 is special */
deepen_since == -1)
@@ -785,8 +785,8 @@ static void receive_needs(void)
}
if (skip_prefix(line, "deepen-not ", &arg)) {
char *ref = NULL;
- unsigned char sha1[20];
- if (expand_ref(arg, strlen(arg), sha1, &ref) != 1)
+ struct object_id oid;
+ if (expand_ref(arg, strlen(arg), oid.hash, &ref) != 1)
die("git upload-pack: ambiguous deepen-not: %s", line);
string_list_append(&deepen_not, ref);
free(ref);
@@ -794,7 +794,7 @@ static void receive_needs(void)
continue;
}
if (!skip_prefix(line, "want ", &arg) ||
- get_sha1_hex(arg, sha1_buf))
+ get_oid_hex(arg, &oid_buf))
die("git upload-pack: protocol error, "
"expected to get sha, not '%s'", line);
@@ -821,13 +821,13 @@ static void receive_needs(void)
if (parse_feature_request(features, "include-tag"))
use_include_tag = 1;
- o = parse_object(sha1_buf);
+ o = parse_object(&oid_buf);
if (!o) {
packet_write_fmt(1,
"ERR upload-pack: not our ref %s",
- sha1_to_hex(sha1_buf));
+ oid_to_hex(&oid_buf));
die("git upload-pack: not our ref %s",
- sha1_to_hex(sha1_buf));
+ oid_to_hex(&oid_buf));
}
if (!(o->flags & WANTED)) {
o->flags |= WANTED;
@@ -863,7 +863,7 @@ static void receive_needs(void)
argv_array_push(&av, "rev-list");
if (deepen_since)
- argv_array_pushf(&av, "--max-age=%lu", deepen_since);
+ argv_array_pushf(&av, "--max-age=%"PRItime, deepen_since);
if (deepen_not.nr) {
argv_array_push(&av, "--not");
for (i = 0; i < deepen_not.nr; i++) {
@@ -883,7 +883,7 @@ static void receive_needs(void)
if (shallows.nr > 0) {
int i;
for (i = 0; i < shallows.nr; i++)
- register_shallow(shallows.objects[i].item->oid.hash);
+ register_shallow(&shallows.objects[i].item->oid);
}
shallow_nr += shallows.nr;
diff --git a/usage.c b/usage.c
index ad6d2910fb..2f87ca69a8 100644
--- a/usage.c
+++ b/usage.c
@@ -6,12 +6,9 @@
#include "git-compat-util.h"
#include "cache.h"
-static FILE *error_handle;
-
void vreportf(const char *prefix, const char *err, va_list params)
{
char msg[4096];
- FILE *fh = error_handle ? error_handle : stderr;
char *p;
vsnprintf(msg, sizeof(msg), err, params);
@@ -19,7 +16,7 @@ void vreportf(const char *prefix, const char *err, va_list params)
if (iscntrl(*p) && *p != '\t' && *p != '\n')
*p = '?';
}
- fprintf(fh, "%s%s\n", prefix, msg);
+ fprintf(stderr, "%s%s\n", prefix, msg);
}
static NORETURN void usage_builtin(const char *err, va_list params)
@@ -88,11 +85,6 @@ void set_die_is_recursing_routine(int (*routine)(void))
die_is_recursing = routine;
}
-void set_error_handle(FILE *fh)
-{
- error_handle = fh;
-}
-
void NORETURN usagef(const char *err, ...)
{
va_list params;
@@ -201,3 +193,35 @@ void warning(const char *warn, ...)
warn_routine(warn, params);
va_end(params);
}
+
+static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_list params)
+{
+ char prefix[256];
+
+ /* truncation via snprintf is OK here */
+ if (file)
+ snprintf(prefix, sizeof(prefix), "BUG: %s:%d: ", file, line);
+ else
+ snprintf(prefix, sizeof(prefix), "BUG: ");
+
+ vreportf(prefix, fmt, params);
+ abort();
+}
+
+#ifdef HAVE_VARIADIC_MACROS
+NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ BUG_vfl(file, line, fmt, ap);
+ va_end(ap);
+}
+#else
+NORETURN void BUG(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ BUG_vfl(NULL, 0, fmt, ap);
+ va_end(ap);
+}
+#endif
diff --git a/vcs-svn/fast_export.c b/vcs-svn/fast_export.c
index 97cba39cdf..5a89db30e3 100644
--- a/vcs-svn/fast_export.c
+++ b/vcs-svn/fast_export.c
@@ -68,12 +68,12 @@ void fast_export_modify(const char *path, uint32_t mode, const char *dataref)
}
void fast_export_begin_note(uint32_t revision, const char *author,
- const char *log, unsigned long timestamp, const char *note_ref)
+ const char *log, timestamp_t timestamp, const char *note_ref)
{
static int firstnote = 1;
size_t loglen = strlen(log);
printf("commit %s\n", note_ref);
- printf("committer %s <%s@%s> %lu +0000\n", author, author, "local", timestamp);
+ printf("committer %s <%s@%s> %"PRItime" +0000\n", author, author, "local", timestamp);
printf("data %"PRIuMAX"\n", (uintmax_t)loglen);
fwrite(log, loglen, 1, stdout);
if (firstnote) {
@@ -93,7 +93,7 @@ static char gitsvnline[MAX_GITSVN_LINE_LEN];
void fast_export_begin_commit(uint32_t revision, const char *author,
const struct strbuf *log,
const char *uuid, const char *url,
- unsigned long timestamp, const char *local_ref)
+ timestamp_t timestamp, const char *local_ref)
{
static const struct strbuf empty = STRBUF_INIT;
if (!log)
@@ -107,7 +107,7 @@ void fast_export_begin_commit(uint32_t revision, const char *author,
}
printf("commit %s\n", local_ref);
printf("mark :%"PRIu32"\n", revision);
- printf("committer %s <%s@%s> %lu +0000\n",
+ printf("committer %s <%s@%s> %"PRItime" +0000\n",
*author ? author : "nobody",
*author ? author : "nobody",
*uuid ? uuid : "local", timestamp);
diff --git a/vcs-svn/fast_export.h b/vcs-svn/fast_export.h
index c8b5adb811..b9a3b71c99 100644
--- a/vcs-svn/fast_export.h
+++ b/vcs-svn/fast_export.h
@@ -11,10 +11,10 @@ void fast_export_delete(const char *path);
void fast_export_modify(const char *path, uint32_t mode, const char *dataref);
void fast_export_note(const char *committish, const char *dataref);
void fast_export_begin_note(uint32_t revision, const char *author,
- const char *log, unsigned long timestamp, const char *note_ref);
+ const char *log, timestamp_t timestamp, const char *note_ref);
void fast_export_begin_commit(uint32_t revision, const char *author,
const struct strbuf *log, const char *uuid,const char *url,
- unsigned long timestamp, const char *local_ref);
+ timestamp_t timestamp, const char *local_ref);
void fast_export_end_commit(uint32_t revision);
void fast_export_data(uint32_t mode, off_t len, struct line_buffer *input);
void fast_export_buf_to_data(const struct strbuf *data);
diff --git a/vcs-svn/svndump.c b/vcs-svn/svndump.c
index e4b395963b..1846685a21 100644
--- a/vcs-svn/svndump.c
+++ b/vcs-svn/svndump.c
@@ -47,7 +47,7 @@ static struct {
static struct {
uint32_t revision;
- unsigned long timestamp;
+ timestamp_t timestamp;
struct strbuf log, author, note;
} rev_ctx;
diff --git a/walker.c b/walker.c
index 2c86e406f9..274f1a4935 100644
--- a/walker.c
+++ b/walker.c
@@ -47,12 +47,12 @@ static int process_tree(struct walker *walker, struct tree *tree)
if (S_ISGITLINK(entry.mode))
continue;
if (S_ISDIR(entry.mode)) {
- struct tree *tree = lookup_tree(entry.oid->hash);
+ struct tree *tree = lookup_tree(entry.oid);
if (tree)
obj = &tree->object;
}
else {
- struct blob *blob = lookup_blob(entry.oid->hash);
+ struct blob *blob = lookup_blob(entry.oid);
if (blob)
obj = &blob->object;
}
@@ -180,7 +180,7 @@ static int loop(struct walker *walker)
}
}
if (!obj->type)
- parse_object(obj->oid.hash);
+ parse_object(&obj->oid);
if (process_object(walker, obj))
return -1;
}
@@ -206,7 +206,7 @@ static int interpret_target(struct walker *walker, char *target, unsigned char *
static int mark_complete(const char *path, const struct object_id *oid,
int flag, void *cb_data)
{
- struct commit *commit = lookup_commit_reference_gently(oid->hash, 1);
+ struct commit *commit = lookup_commit_reference_gently(oid, 1);
if (commit) {
commit->object.flags |= COMPLETE;
diff --git a/worktree.c b/worktree.c
index 89a81b13de..2801c6d52b 100644
--- a/worktree.c
+++ b/worktree.c
@@ -19,54 +19,25 @@ void free_worktrees(struct worktree **worktrees)
free (worktrees);
}
-/*
- * read 'path_to_ref' into 'ref'. Also if is_detached is not NULL,
- * set is_detached to 1 (0) if the ref is detached (is not detached).
- *
- * $GIT_COMMON_DIR/$symref (e.g. HEAD) is practically outside $GIT_DIR so
- * for linked worktrees, `resolve_ref_unsafe()` won't work (it uses
- * git_path). Parse the ref ourselves.
- *
- * return -1 if the ref is not a proper ref, 0 otherwise (success)
- */
-static int parse_ref(char *path_to_ref, struct strbuf *ref, int *is_detached)
-{
- if (is_detached)
- *is_detached = 0;
- if (!strbuf_readlink(ref, path_to_ref, 0)) {
- /* HEAD is symbolic link */
- if (!starts_with(ref->buf, "refs/") ||
- check_refname_format(ref->buf, 0))
- return -1;
- } else if (strbuf_read_file(ref, path_to_ref, 0) >= 0) {
- /* textual symref or detached */
- if (!starts_with(ref->buf, "ref:")) {
- if (is_detached)
- *is_detached = 1;
- } else {
- strbuf_remove(ref, 0, strlen("ref:"));
- strbuf_trim(ref);
- if (check_refname_format(ref->buf, 0))
- return -1;
- }
- } else
- return -1;
- return 0;
-}
-
/**
- * Add the head_sha1 and head_ref (if not detached) to the given worktree
+ * Update head_sha1, head_ref and is_detached of the given worktree
*/
-static void add_head_info(struct strbuf *head_ref, struct worktree *worktree)
+static void add_head_info(struct worktree *wt)
{
- if (head_ref->len) {
- if (worktree->is_detached) {
- get_sha1_hex(head_ref->buf, worktree->head_sha1);
- } else {
- resolve_ref_unsafe(head_ref->buf, 0, worktree->head_sha1, NULL);
- worktree->head_ref = strbuf_detach(head_ref, NULL);
- }
- }
+ int flags;
+ const char *target;
+
+ target = refs_resolve_ref_unsafe(get_worktree_ref_store(wt),
+ "HEAD",
+ RESOLVE_REF_READING,
+ wt->head_sha1, &flags);
+ if (!target)
+ return;
+
+ if (flags & REF_ISSYMREF)
+ wt->head_ref = xstrdup(target);
+ else
+ wt->is_detached = 1;
}
/**
@@ -77,9 +48,7 @@ static struct worktree *get_main_worktree(void)
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;
struct strbuf worktree_path = STRBUF_INIT;
- struct strbuf head_ref = STRBUF_INIT;
int is_bare = 0;
- int is_detached = 0;
strbuf_add_absolute_path(&worktree_path, get_git_common_dir());
is_bare = !strbuf_strip_suffix(&worktree_path, "/.git");
@@ -91,13 +60,10 @@ static struct worktree *get_main_worktree(void)
worktree = xcalloc(1, sizeof(*worktree));
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->is_bare = is_bare;
- worktree->is_detached = is_detached;
- if (!parse_ref(path.buf, &head_ref, &is_detached))
- add_head_info(&head_ref, worktree);
+ add_head_info(worktree);
strbuf_release(&path);
strbuf_release(&worktree_path);
- strbuf_release(&head_ref);
return worktree;
}
@@ -106,8 +72,6 @@ static struct worktree *get_linked_worktree(const char *id)
struct worktree *worktree = NULL;
struct strbuf path = STRBUF_INIT;
struct strbuf worktree_path = STRBUF_INIT;
- struct strbuf head_ref = STRBUF_INIT;
- int is_detached = 0;
if (!id)
die("Missing linked worktree name");
@@ -127,19 +91,14 @@ static struct worktree *get_linked_worktree(const char *id)
strbuf_reset(&path);
strbuf_addf(&path, "%s/worktrees/%s/HEAD", get_git_common_dir(), id);
- if (parse_ref(path.buf, &head_ref, &is_detached) < 0)
- goto done;
-
worktree = xcalloc(1, sizeof(*worktree));
worktree->path = strbuf_detach(&worktree_path, NULL);
worktree->id = xstrdup(id);
- worktree->is_detached = is_detached;
- add_head_info(&head_ref, worktree);
+ add_head_info(worktree);
done:
strbuf_release(&path);
strbuf_release(&worktree_path);
- strbuf_release(&head_ref);
return worktree;
}
@@ -337,8 +296,6 @@ const struct worktree *find_shared_symref(const char *symref,
const char *target)
{
const struct worktree *existing = NULL;
- struct strbuf path = STRBUF_INIT;
- struct strbuf sb = STRBUF_INIT;
static struct worktree **worktrees;
int i = 0;
@@ -348,6 +305,11 @@ const struct worktree *find_shared_symref(const char *symref,
for (i = 0; worktrees[i]; i++) {
struct worktree *wt = worktrees[i];
+ const char *symref_target;
+ unsigned char sha1[20];
+ struct ref_store *refs;
+ int flags;
+
if (wt->is_bare)
continue;
@@ -362,25 +324,15 @@ const struct worktree *find_shared_symref(const char *symref,
}
}
- strbuf_reset(&path);
- strbuf_reset(&sb);
- strbuf_addf(&path, "%s/%s",
- get_worktree_git_dir(wt),
- symref);
-
- if (parse_ref(path.buf, &sb, NULL)) {
- continue;
- }
-
- if (!strcmp(sb.buf, target)) {
+ refs = get_worktree_ref_store(wt);
+ symref_target = refs_resolve_ref_unsafe(refs, symref, 0,
+ sha1, &flags);
+ if ((flags & REF_ISSYMREF) && !strcmp(symref_target, target)) {
existing = wt;
break;
}
}
- strbuf_release(&path);
- strbuf_release(&sb);
-
return existing;
}
diff --git a/worktree.h b/worktree.h
index 6bfb985203..5ea5e503fb 100644
--- a/worktree.h
+++ b/worktree.h
@@ -4,7 +4,7 @@
struct worktree {
char *path;
char *id;
- char *head_ref;
+ char *head_ref; /* NULL if HEAD is broken or detached */
char *lock_reason; /* internal use */
unsigned char head_sha1[20];
int is_detached;
diff --git a/wt-status.c b/wt-status.c
index 068de38b51..25aafc35c8 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -665,7 +665,7 @@ static void wt_status_collect_untracked(struct wt_status *s)
dir.untracked = the_index.untracked;
setup_standard_excludes(&dir);
- fill_directory(&dir, &s->pathspec);
+ fill_directory(&dir, &the_index, &s->pathspec);
for (i = 0; i < dir.nr; i++) {
struct dir_entry *ent = dir.entries[i];
@@ -1003,7 +1003,7 @@ static void wt_longstatus_print_tracking(struct wt_status *s)
color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "%c",
comment_line_char);
else
- fputs("", s->fp);
+ fputs("\n", s->fp);
}
static int has_unmerged(struct wt_status *s)
@@ -1389,7 +1389,7 @@ struct grab_1st_switch_cbdata {
};
static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct grab_1st_switch_cbdata *cb = cb_data;
@@ -1430,7 +1430,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
/* sha1 is a commit? match without further lookup */
(!oidcmp(&cb.noid, &oid) ||
/* perhaps sha1 is a tag, try to dereference to a commit */
- ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL &&
+ ((commit = lookup_commit_reference_gently(&oid, 1)) != NULL &&
!oidcmp(&cb.noid, &commit->object.oid)))) {
const char *from = ref;
if (!skip_prefix(from, "refs/tags/", &from))