Merge branch 'rf/completion'
authorJunio C Hamano <gitster@pobox.com>
Tue, 13 Jun 2017 20:47:09 +0000 (13:47 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 13 Jun 2017 20:47:09 +0000 (13:47 -0700)
Completion updates.

* rf/completion:
completion: add git config credentialCache.ignoreSIGHUP
completion: add git config credential completions
completion: add git config advice completions
completion: add git config am.threeWay completion
completion: add git config core completions
completion: add git config gc completions

323 files changed:
.gitattributes
.travis.yml
Documentation/CodingGuidelines
Documentation/RelNotes/2.13.1.txt [new file with mode: 0644]
Documentation/RelNotes/2.14.0.txt [new file with mode: 0644]
Documentation/config.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-clone.txt
Documentation/git-filter-branch.txt
Documentation/git-grep.txt
Documentation/git-interpret-trailers.txt
Documentation/git-pull.txt
Documentation/git-read-tree.txt
Documentation/git-repack.txt
Documentation/git-rev-parse.txt
Documentation/git-send-email.txt
Documentation/git-svn.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitcredentials.txt
Documentation/githooks.txt
Documentation/gitweb.txt
Documentation/pretty-formats.txt
Documentation/rev-list-options.txt
Documentation/technical/api-directory-listing.txt
Documentation/technical/api-parse-options.txt
Documentation/technical/api-sub-process.txt [new file with mode: 0644]
Documentation/technical/pack-protocol.txt
GIT-VERSION-GEN
Makefile
RelNotes
apply.c
archive-tar.c
archive-zip.c
archive.c
archive.h
bisect.c
blame.c [new file with mode: 0644]
blame.h [new file with mode: 0644]
blob.c
blob.h
branch.c
builtin.h
builtin/add.c
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/cat-file.c
builtin/check-ignore.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/diff-files.c
builtin/diff-index.c
builtin/diff-tree.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/index-pack.c
builtin/log.c
builtin/ls-files.c
builtin/ls-tree.c
builtin/mailsplit.c
builtin/merge-base.c
builtin/merge-tree.c
builtin/merge.c
builtin/mktree.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/pack-redundant.c
builtin/prune.c
builtin/pull.c
builtin/push.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/repack.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/rm.c
builtin/show-branch.c
builtin/submodule--helper.c
builtin/tag.c
builtin/unpack-objects.c
builtin/update-index.c
builtin/verify-commit.c
builtin/worktree.c
bulk-checkin.c
bundle.c
bundle.h
cache-tree.c
cache-tree.h
cache.h
ci/run-windows-build.sh
ci/test-documentation.sh
commit.c
commit.h
compat/mingw.c
compat/mingw.h
compat/winansi.c
config.c
config.mak.uname
configure.ac
connect.c
contrib/completion/.gitattributes [new file with mode: 0644]
contrib/completion/git-completion.bash
contrib/persistent-https/README
contrib/workdir/.gitattributes [new file with mode: 0644]
convert.c
credential-cache--daemon.c
date.c
diff-lib.c
diff.c
diff.h
dir.c
dir.h
entry.c
environment.c
fast-import.c
fetch-pack.c
fsck.c
git-add--interactive.perl
git-compat-util.h
git-filter-branch.sh
git-gui/.gitattributes
git-rebase--interactive.sh
git-send-email.perl
gitweb/gitweb.perl
grep.c
grep.h
help.c
http-backend.c
http-push.c
line-log.c
list-objects.c
lockfile.h
log-tree.c
mailinfo.c
merge-recursive.c
merge.c
notes-cache.c
notes-cache.h
notes-merge.c
notes-utils.c
object.c
object.h
pack-bitmap-write.c
pack-bitmap.c
pack-check.c
pack-objects.c
pack-write.c
pack.h
parse-options-cb.c
parse-options.c
patch-ids.c
pathspec.c
pathspec.h
pkt-line.c
pkt-line.h
pretty.c
reachable.c
reachable.h
read-cache.c
ref-filter.c
ref-filter.h
reflog-walk.c
refs.c
refs.h
refs/files-backend.c
refs/iterator.c
refs/ref-cache.c
refs/ref-cache.h
refs/refs-internal.h
remote.c
revision.c
revision.h
run-command.c
run-command.h
sequencer.c
server-info.c
setup.c
sha1_name.c
sha1dc/.gitattributes [new file with mode: 0644]
sha1dc/sha1.c
sha1dc/sha1.h
sha1dc/ubc_check.c
sha1dc/ubc_check.h
sha1dc_git.c [new file with mode: 0644]
sha1dc_git.h [new file with mode: 0644]
shallow.c
split-index.c
split-index.h
string-list.c
string-list.h
sub-process.c [new file with mode: 0644]
sub-process.h [new file with mode: 0644]
submodule.c
submodule.h
t/.gitattributes
t/README
t/helper/test-date.c
t/helper/test-dump-cache-tree.c
t/helper/test-match-trees.c
t/helper/test-parse-options.c
t/helper/test-ref-store.c
t/lib-submodule-update.sh
t/perf/README
t/perf/p0004-lazy-init-name-hash.sh
t/perf/p0100-globbing.sh [new file with mode: 0755]
t/perf/p3400-rebase.sh
t/perf/p4220-log-grep-engines.sh [new file with mode: 0755]
t/perf/p4221-log-grep-engines-fixed.sh [new file with mode: 0755]
t/perf/p7820-grep-engines.sh [new file with mode: 0755]
t/perf/p7821-grep-engines-fixed.sh [new file with mode: 0755]
t/perf/perf-lib.sh
t/perf/run
t/t0006-date.sh
t/t0025-crlf-auto.sh [deleted file]
t/t0027-auto-crlf.sh
t/t0061-run-command.sh
t/t0203-gettext-setlocale-sanity.sh
t/t1013-read-tree-submodule.sh
t/t1300-repo-config.sh
t/t1305-config-include.sh
t/t1309-early-config.sh
t/t1405-main-ref-store.sh
t/t1406-submodule-ref-store.sh
t/t1407-worktree-ref-store.sh [new file with mode: 0755]
t/t1430-bad-ref-name.sh
t/t1601-index-bogus.sh [new file with mode: 0755]
t/t2013-checkout-submodule.sh
t/t3070-wildmatch.sh
t/t3200-branch.sh
t/t3203-branch-output.sh
t/t3404-rebase-interactive.sh
t/t3415-rebase-autosquash.sh
t/t3511-cherry-pick-x.sh
t/t3600-rm.sh
t/t3901-8859-1.txt [deleted file]
t/t3901-i18n-patch.sh
t/t3901-utf8.txt [deleted file]
t/t3901/8859-1.txt [new file with mode: 0755]
t/t3901/utf8.txt [new file with mode: 0755]
t/t3903-stash.sh
t/t4038-diff-combined.sh
t/t4051-diff-function-context.sh
t/t4060-diff-submodule-option-diff-format.sh
t/t4061-diff-indent.sh
t/t4063-diff-blobs.sh [new file with mode: 0755]
t/t4202-log.sh
t/t4205-log-pretty-formats.sh
t/t5000-tar-tree.sh
t/t5004-archive-corner-cases.sh
t/t5004/big-pack.zip [new file with mode: 0644]
t/t5300-pack-object.sh
t/t5310-pack-bitmaps.sh
t/t5316-pack-delta-depth.sh
t/t5400-send-pack.sh
t/t5500-fetch-pack.sh
t/t5520-pull.sh
t/t5526-fetch-submodules.sh
t/t5531-deep-submodule-push.sh
t/t5534-push-signed.sh
t/t5545-push-options.sh
t/t5550-http-fetch-dumb.sh
t/t5611-clone-config.sh
t/t5612-clone-refspec.sh
t/t5614-clone-submodules-shallow.sh [new file with mode: 0755]
t/t5614-clone-submodules.sh [deleted file]
t/t6134-pathspec-in-submodule.sh
t/t6501-freshen-objects.sh
t/t7004-tag.sh
t/t7008-grep-binary.sh
t/t7009-filter-branch-null-sha1.sh
t/t7061-wtstatus-ignore.sh
t/t7063-status-untracked-cache.sh
t/t7112-reset-submodule.sh
t/t7300-clean.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7508-status.sh
t/t7509-commit.sh
t/t7513-interpret-trailers.sh
t/t7800-difftool.sh
t/t7810-grep.sh
t/t7812-grep-icase-non-ascii.sh
t/t7813-grep-icase-iso.sh
t/t7814-grep-recurse-submodules.sh
t/t9001-send-email.sh
t/t9350-fast-export.sh
t/t9500-gitweb-standalone-no-errors.sh
t/test-lib.sh
tag.c
tag.h
transport.c
tree-walk.c
tree.c
tree.h
unpack-trees.c
upload-pack.c
usage.c
vcs-svn/fast_export.c
vcs-svn/fast_export.h
vcs-svn/svndump.c
walker.c
worktree.c
worktree.h
wrapper.c
wt-status.c
wt-status.h
index 320e33c327c6f597bcfd255b13876f21b0b2d8aa..8ce9c6b8888fe6c12949d30e3e8b461cb67bb43f 100644 (file)
@@ -1,3 +1,9 @@
 * whitespace=!indent,trail,space
 *.[ch] whitespace=indent,trail,space diff=cpp
-*.sh whitespace=indent,trail,space
+*.sh whitespace=indent,trail,space eol=lf
+*.perl eol=lf
+*.pm eol=lf
+/Documentation/git-*.txt eol=lf
+/command-list.txt eol=lf
+/GIT-VERSION-GEN eol=lf
+/mergetools/* eol=lf
index 1b32c98f2cd71dc3cc559fd7efc709a5bd65ac1f..278943d14a244b6b748078ff9ee18a17f58e7b45 100644 (file)
@@ -39,6 +39,11 @@ env:
 
 matrix:
   include:
+    - env: GETTEXT_POISON=YesPlease
+      os: linux
+      compiler:
+      addons:
+      before_install:
     - env: Windows
       os: linux
       compiler:
@@ -52,6 +57,7 @@ matrix:
       after_failure:
     - env: Linux32
       os: linux
+      compiler:
       services:
         - docker
       before_install:
@@ -86,14 +92,14 @@ matrix:
       after_failure:
     - env: Documentation
       os: linux
-      compiler: clang
+      compiler:
       addons:
         apt:
           packages:
           - asciidoc
           - xmlto
       before_install:
-      before_script:
+      before_script: gem install asciidoctor
       script: ci/test-documentation.sh
       after_failure:
 
@@ -134,12 +140,14 @@ before_install:
     p4 -V | grep Rev.;
     echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)";
     git-lfs version;
-    mkdir -p $HOME/travis-cache;
-    ln -s $HOME/travis-cache/.prove t/.prove;
 
 before_script: make --jobs=2
 
-script: make --quiet test
+script:
+  - >
+    mkdir -p $HOME/travis-cache;
+    ln -s $HOME/travis-cache/.prove t/.prove;
+    make --quiet test;
 
 after_failure:
   - >
index a4191aa3889000ed844678331e5fd7f9fc628ba4..c4cb5ff0d477938b8fd49749c3589c5afbb04221 100644 (file)
@@ -24,7 +24,7 @@ code.  For Git in general, a few rough rules are:
 
    "Once it _is_ in the tree, it's not really worth the patch noise to
    go and fix it up."
-   Cf. http://article.gmane.org/gmane.linux.kernel/943020
+   Cf. http://lkml.iu.edu/hypermail/linux/kernel/1001.3/01069.html
 
 Make your code readable and sensible, and don't try to be clever.
 
@@ -256,12 +256,12 @@ For C programs:
 
    Note however that a comment that explains a translatable string to
    translators uses a convention of starting with a magic token
-   "TRANSLATORS: " immediately after the opening delimiter, even when
-   it spans multiple lines.  We do not add an asterisk at the beginning
-   of each line, either.  E.g.
+   "TRANSLATORS: ", e.g.
 
-       /* TRANSLATORS: here is a comment that explains the string
-          to be translated, that follows immediately after it */
+       /*
+        * TRANSLATORS: here is a comment that explains the string to
+        * be translated, that follows immediately after it.
+        */
        _("Here is a translatable string explained by the above.");
 
  - Double negation is often harder to understand than no negation
diff --git a/Documentation/RelNotes/2.13.1.txt b/Documentation/RelNotes/2.13.1.txt
new file mode 100644 (file)
index 0000000..ed7cd97
--- /dev/null
@@ -0,0 +1,114 @@
+Git v2.13.1 Release Notes
+=========================
+
+Fixes since v2.13
+-----------------
+
+ * 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.
+
+ * Update tests to pass under GETTEXT_POISON (a mechanism to ensure
+   that output strings that should not be translated are not
+   translated by mistake), and tell TravisCI to run them.
+
+ * Setting "log.decorate=false" in the configuration file did not take
+   effect in v2.13, which has been corrected.
+
+ * An earlier update to test 7400 needed to be skipped on CYGWIN.
+
+ * 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.
+
+ * "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.
+
+ * The codepath in "git am" that is used when running "git rebase"
+   leaked memory held for the log message of the commits being rebased.
+
+ * "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.
+
+ * Clarify documentation for include.path and includeIf.<condition>.path
+   configuration variables.
+
+ * 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)
+
+ * A few http:// links that are redirected to https:// in the
+   documentation have been updated to https:// links.
+
+ * 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.
+
+ * Fix memory leaks pointed out by Coverity (and people).
+
+ * The receive-pack program now makes sure that the push certificate
+   records the same set of push options used for pushing.
+
+ * "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 shell completion script (in contrib/) learned "git stash" has
+   a new "push" subcommand.
+
+ * Travis CI gained a task to format the documentation with both
+   AsciiDoc and AsciiDoctor.
+
+ * Update the C style recommendation for notes for translators, as
+   recent versions of gettext tools can work with our style of
+   multi-line comments.
+
+ * "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.
+
+ * A few codepaths in "checkout" and "am" working on an unborn branch
+   tried to access an uninitialized piece of memory.
+
+ * "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.
+
+ * "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.
+
+ * "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.
+
+ * 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.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.14.0.txt b/Documentation/RelNotes/2.14.0.txt
new file mode 100644 (file)
index 0000000..e4ca72e
--- /dev/null
@@ -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).
index 475e874d51550eba26a578c0ce3b17b61384fddc..f6278a5ae6a1f554ddb8b9861d9067181b928bb5 100644 (file)
@@ -79,14 +79,20 @@ escape sequences) are invalid.
 Includes
 ~~~~~~~~
 
+The `include` and `includeIf` sections allow you to include config
+directives from another source. These sections behave identically to
+each other with the exception that `includeIf` sections may be ignored
+if their condition does not evaluate to true; see "Conditional includes"
+below.
+
 You can include a config file from another by setting the special
-`include.path` variable to the name of the file to be included. The
-variable takes a pathname as its value, and is subject to tilde
-expansion. `include.path` can be given multiple times.
+`include.path` (or `includeIf.*.path`) variable to the name of the file
+to be included. The variable takes a pathname as its value, and is
+subject to tilde expansion. These variables can be given multiple times.
 
-The included file is expanded immediately, as if its contents had been
-found at the location of the include directive. If the value of the
-`include.path` variable is a relative path, the path is considered to
+The contents of the included file are inserted immediately, as if they
+had been found at the location of the include directive. If the value of the
+variable is a relative path, the path is considered to
 be relative to the configuration file in which the include directive
 was found.  See below for examples.
 
@@ -95,8 +101,7 @@ Conditional includes
 
 You can include a config file from another conditionally by setting a
 `includeIf.<condition>.path` variable to the name of the file to be
-included. The variable's value is treated the same way as
-`include.path`. `includeIf.<condition>.path` can be given multiple times.
+included.
 
 The condition starts with a keyword followed by a colon and some data
 whose format and meaning depends on the keyword. Supported keywords
@@ -140,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.
 
@@ -167,8 +182,8 @@ Example
 
        [include]
                path = /path/to/foo.inc ; include by absolute path
-               path = foo ; expand "foo" relative to the current file
-               path = ~/foo ; expand "foo" in your `$HOME` directory
+               path = foo.inc ; find "foo.inc" relative to the current file
+               path = ~/foo.inc ; find "foo.inc" in your `$HOME` directory
 
        ; include if $GIT_DIR is /path/to/foo/.git
        [includeIf "gitdir:/path/to/foo/.git"]
@@ -182,6 +197,12 @@ Example
        [includeIf "gitdir:~/to/group/"]
                path = /path/to/foo.inc
 
+       ; relative paths are always relative to the including
+       ; file (if the condition is true); their location is not
+       ; affected by the condition
+       [includeIf "gitdir:/path/to/group/"]
+               path = foo.inc
+
 Values
 ~~~~~~
 
@@ -334,7 +355,7 @@ core.fileMode::
        is to be honored.
 +
 Some filesystems lose the executable bit when a file that is
-marked as executable is checked out, or checks out an
+marked as executable is checked out, or checks out a
 non-executable file with executable bit on.
 linkgit:git-clone[1] or linkgit:git-init[1] probe the filesystem
 to see if it handles the executable bit correctly
@@ -862,6 +883,7 @@ core.abbrev::
        computed based on the approximate number of packed objects
        in your repository, which hopefully is enough for
        abbreviated object names to stay unique for some time.
+       The minimum length is 4.
 
 add.ignoreErrors::
 add.ignore-errors (deprecated)::
@@ -1137,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::
@@ -2140,6 +2165,10 @@ log.showRoot::
        Tools like linkgit:git-log[1] or linkgit:git-whatchanged[1], which
        normally hide the root commit will now show it. True by default.
 
+log.showSignature::
+       If true, makes linkgit:git-log[1], linkgit:git-show[1], and
+       linkgit:git-whatchanged[1] assume `--show-signature`.
+
 log.mailmap::
        If true, makes linkgit:git-log[1], linkgit:git-show[1], and
        linkgit:git-whatchanged[1] assume `--use-mailmap`.
@@ -2620,9 +2649,8 @@ receive.advertiseAtomic::
        capability, set this variable to false.
 
 receive.advertisePushOptions::
-       By default, git-receive-pack will advertise the push options
-       capability to its clients. If you don't want to advertise this
-       capability, set this variable to false.
+       When set to true, git-receive-pack will advertise the push options
+       capability to its clients. False by default.
 
 receive.autogc::
        By default, git-receive-pack will run "git-gc --auto" after
@@ -3063,6 +3091,11 @@ submodule.active::
        submodule's path to determine if the submodule is of interest to git
        commands.
 
+submodule.recurse::
+       Specifies if commands recurse into submodules by default. This
+       applies to all commands that have a `--recurse-submodules` option.
+       Defaults to false.
+
 submodule.fetchJobs::
        Specifies how many submodules are fetched/cloned at the same time.
        A positive integer allows up to that number of submodules fetched
@@ -3208,6 +3241,13 @@ url.<base>.insteadOf::
        the best alternative for the particular user, even for a
        never-before-seen repository on the site.  When more than one
        insteadOf strings match a given URL, the longest match is used.
++
+Note that any protocol restrictions will be applied to the rewritten
+URL. If the rewrite changes the URL to use a custom protocol or remote
+helper, you may need to adjust the `protocol.*.allow` config to permit
+the request.  In particular, protocols you expect to use for submodules
+must be set to `always` rather than the default of `user`. See the
+description of `protocol.allow` above.
 
 url.<base>.pushInsteadOf::
        Any URL that starts with this value will not be pushed to;
index 8ac75fcc25934bbf594d776f919be6d9dbb1013b..78479b003eb3a340da99c533aece8ee9fc19360c 100644 (file)
@@ -1350,9 +1350,9 @@ References
 - [[[1]]] https://www.nist.gov/sites/default/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'.  Nist Planning Report 02-3], see Executive Summary and Chapter 8.
 - [[[2]]] http://www.oracle.com/technetwork/java/codeconvtoc-136057.html['Code Conventions for the Java Programming Language'. Sun Microsystems.]
 - [[[3]]] https://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.]
-- [[[4]]] http://article.gmane.org/gmane.comp.version-control.git/45195/[Junio C Hamano. 'Automated bisect success story'. Gmane.]
+- [[[4]]] https://public-inbox.org/git/7vps5xsbwp.fsf_-_@assigned-by-dhcp.cox.net/[Junio C Hamano. 'Automated bisect success story'.]
 - [[[5]]] https://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.]
 - [[[6]]] https://lwn.net/Articles/277872/[Jonathan Corbet. 'Bisection divides users and developers'. LWN.net.]
-- [[[7]]] http://article.gmane.org/gmane.linux.scsi/36652/[Ingo Molnar. 'Re: BUG 2.6.23-rc3 can't see sd partitions on Alpha'. Gmane.]
+- [[[7]]] http://marc.info/?l=linux-kernel&m=119702753411680&w=2[Ingo Molnar. 'Re: BUG 2.6.23-rc3 can't see sd partitions on Alpha'. Linux-kernel mailing list.]
 - [[[8]]] https://www.kernel.org/pub/software/scm/git/docs/git-bisect.html[Junio C Hamano and the git-list. 'git-bisect(1) Manual Page'. Linux Kernel Archives.]
 - [[[9]]] https://github.com/Ealdwulf/bbchop[Ealdwulf. 'bbchop'. GitHub.]
index 30052cce49947914f71b16aa0474bfaa36d13b09..83c8e9b394a3a83639852210e2f6e237a0077ed2 100644 (file)
@@ -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
index 6e4bb022043faeddd363c899872e7de96c926c1c..7b695dbb7267bb8ab3497af133331dd8fbb3c147 100644 (file)
@@ -86,8 +86,7 @@ OPTIONS
        This filter may be used if you only need to modify the environment
        in which the commit will be performed.  Specifically, you might
        want to rewrite the author/committer name/email/time environment
-       variables (see linkgit:git-commit-tree[1] for details).  Do not forget
-       to re-export the variables.
+       variables (see linkgit:git-commit-tree[1] for details).
 
 --tree-filter <command>::
        This is the filter for rewriting the tree and its contents.
@@ -340,12 +339,10 @@ git filter-branch --env-filter '
        if test "$GIT_AUTHOR_EMAIL" = "root@localhost"
        then
                GIT_AUTHOR_EMAIL=john@example.com
-               export GIT_AUTHOR_EMAIL
        fi
        if test "$GIT_COMMITTER_EMAIL" = "root@localhost"
        then
                GIT_COMMITTER_EMAIL=john@example.com
-               export GIT_COMMITTER_EMAIL
        fi
 ' -- --all
 --------------------------------------------------------
index 71f32f35089241bc452cfdccdc6b3e9a30f95366..5033483db496286910dd24507f3374e18ff6c2b5 100644 (file)
@@ -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::
index 09074c75a46b148485f836e1e10d7590f98d54b6..31cdeaecdfde8fb358c35c3a9a2e3c4279d440d0 100644 (file)
@@ -123,7 +123,7 @@ trailer.ifexists::
        same <token> in the message.
 +
 The valid values for this option are: `addIfDifferentNeighbor` (this
-is the default), `addIfDifferent`, `add`, `overwrite` or `doNothing`.
+is the default), `addIfDifferent`, `add`, `replace` or `doNothing`.
 +
 With `addIfDifferentNeighbor`, a new trailer will be added only if no
 trailer with the same (<token>, <value>) pair is above or below the line
index 942af8e0f7166b49ca581eb099b59714f0a1c327..e414185f5a6a302afa359926c7f8c2c6bf5ac3e7 100644 (file)
@@ -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:
 
index ed9d63ef4a35fa5ba70b1b7f1ffd393f517be030..02576d8c0ae6008fc22d7c7c706909efde5856da 100644 (file)
@@ -137,7 +137,7 @@ Merging
 -------
 If `-m` is specified, 'git read-tree' can perform 3 kinds of
 merge, a single tree merge if only 1 tree is given, a
-fast-forward merge with 2 trees, or a 3-way merge if 3 trees are
+fast-forward merge with 2 trees, or a 3-way merge if 3 or more trees are
 provided.
 
 
index 26afe6ed549002f83296e76d8be6502332bc7edf..ae750e9e1149f512dd5889a8081452055ccdb6d7 100644 (file)
@@ -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
index c40c4704486dd9d1a5650562540db7379818ab03..b1293f24bb46f71238f3dee4700340a79b90fca8 100644 (file)
@@ -126,6 +126,12 @@ can be used.
        'git diff-{asterisk}'). In contrast to the `--sq-quote` option,
        the command input is still interpreted as usual.
 
+--short[=length]::
+       Same as `--verify` but shortens the object name to a unique
+       prefix with at least `length` characters. The minimum length
+       is 4, the default is the effective value of the `core.abbrev`
+       configuration variable (see linkgit:git-config[1]).
+
 --not::
        When showing object names, prefix them with '{caret}' and
        strip '{caret}' prefix from the object names that already have
@@ -136,12 +142,6 @@ can be used.
        The option core.warnAmbiguousRefs is used to select the strict
        abbreviation mode.
 
---short::
---short=number::
-       Instead of outputting the full SHA-1 values of object names try to
-       abbreviate them to a shorter unique name. When no length is specified
-       7 is used. The minimum length is 4.
-
 --symbolic::
        Usually the object names are output in SHA-1 form (with
        possible '{caret}' prefix); this option makes them output in a
index 9d66166f69d93e544c949ad80fe0f432fa3f1ffd..bb23b02caf3ee50a32c86d97e0451cd4be6ab232 100644 (file)
@@ -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.
 --
index 9bee9b0c4c53692bab569577d584797c0bd9e217..fba0b4eece46eb56aa926e2957658352e3ba1f10 100644 (file)
@@ -95,6 +95,10 @@ If you still want the old default, you can get it by passing
 `--prefix ""` on the command line (`--prefix=""` may not work if
 your Perl's Getopt::Long is < v2.37).
 
+--ignore-refs=<regex>;;
+       When passed to 'init' or 'clone' this regular expression will
+       be preserved as a config key.  See 'fetch' for a description
+       of `--ignore-refs`.
 --ignore-paths=<regex>;;
        When passed to 'init' or 'clone' this regular expression will
        be preserved as a config key.  See 'fetch' for a description
@@ -138,6 +142,18 @@ the same local time zone.
 --parent;;
        Fetch only from the SVN parent of the current HEAD.
 
+--ignore-refs=<regex>;;
+       Ignore refs for branches or tags matching the Perl regular
+       expression. A "negative look-ahead assertion" like
+       `^refs/remotes/origin/(?!tags/wanted-tag|wanted-branch).*$`
+       can be used to allow only certain refs.
++
+[verse]
+config key: svn-remote.<name>.ignore-refs
++
+If the ignore-refs configuration key is set, and the command-line
+option is also given, both regular expressions will be used.
+
 --ignore-paths=<regex>;;
        This allows one to specify a Perl regular expression that will
        cause skipping of all matching paths from checkout from SVN.
index f8a0b787f47d6e752761fa2a442d4ebcb300e2ed..1eb15afa1ce8a533d65c5f5ace0bcd694d7f6ffd 100644 (file)
@@ -12,7 +12,7 @@ SYNOPSIS
 'git tag' [-a | -s | -u <keyid>] [-f] [-m <msg> | -F <file>]
        <tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
-'git tag' [-n[<num>]] -l [--contains <commit>] [--contains <commit>]
+'git tag' [-n[<num>]] -l [--contains <commit>] [--no-contains <commit>]
        [--points-at <object>] [--column[=<options>] | --no-column]
        [--create-reflog] [--sort=<key>] [--format=<format>]
        [--[no-]merged [<commit>]] [<pattern>...]
index fb10314c1d70c9af47119af6d111d40bdae09e6d..7dd5e03280b09f21f59289b0b00cd6fdee0ca3cb 100644 (file)
@@ -35,516 +35,6 @@ manual page gives you an overview of the command-line command syntax.
 A formatted and hyperlinked copy of the latest Git documentation
 can be viewed at `https://git.github.io/htmldocs/git.html`.
 
-ifdef::stalenotes[]
-[NOTE]
-============
-
-You are reading the documentation for the latest (possibly
-unreleased) version of Git, that is available from the 'master'
-branch of the `git.git` repository.
-Documentation for older releases are available here:
-
-* link:v2.13.0/git.html[documentation for release 2.13]
-
-* release notes for
-  link:RelNotes/2.13.0.txt[2.13].
-
-
-* link:v2.12.3/git.html[documentation for release 2.12.3]
-
-* release notes for
-  link:RelNotes/2.12.3.txt[2.12.3],
-  link:RelNotes/2.12.2.txt[2.12.2],
-  link:RelNotes/2.12.1.txt[2.12.1],
-  link:RelNotes/2.12.0.txt[2.12].
-
-* link:v2.11.1/git.html[documentation for release 2.11.1]
-
-* release notes for
-  link:RelNotes/2.11.2.txt[2.11.2],
-  link:RelNotes/2.11.1.txt[2.11.1],
-  link:RelNotes/2.11.0.txt[2.11].
-
-* link:v2.10.3/git.html[documentation for release 2.10.3]
-
-* release notes for
-  link:RelNotes/2.10.3.txt[2.10.3],
-  link:RelNotes/2.10.2.txt[2.10.2],
-  link:RelNotes/2.10.1.txt[2.10.1],
-  link:RelNotes/2.10.0.txt[2.10].
-
-* link:v2.9.4/git.html[documentation for release 2.9.4]
-
-* release notes for
-  link:RelNotes/2.9.4.txt[2.9.4],
-  link:RelNotes/2.9.3.txt[2.9.3],
-  link:RelNotes/2.9.2.txt[2.9.2],
-  link:RelNotes/2.9.1.txt[2.9.1],
-  link:RelNotes/2.9.0.txt[2.9].
-
-* link:v2.8.5/git.html[documentation for release 2.8.5]
-
-* release notes for
-  link:RelNotes/2.8.5.txt[2.8.5],
-  link:RelNotes/2.8.4.txt[2.8.4],
-  link:RelNotes/2.8.3.txt[2.8.3],
-  link:RelNotes/2.8.2.txt[2.8.2],
-  link:RelNotes/2.8.1.txt[2.8.1],
-  link:RelNotes/2.8.0.txt[2.8].
-
-* link:v2.7.5/git.html[documentation for release 2.7.5]
-
-* release notes for
-  link:RelNotes/2.7.5.txt[2.7.5],
-  link:RelNotes/2.7.4.txt[2.7.4],
-  link:RelNotes/2.7.3.txt[2.7.3],
-  link:RelNotes/2.7.2.txt[2.7.2],
-  link:RelNotes/2.7.1.txt[2.7.1],
-  link:RelNotes/2.7.0.txt[2.7].
-
-* link:v2.6.7/git.html[documentation for release 2.6.7]
-
-* release notes for
-  link:RelNotes/2.6.7.txt[2.6.7],
-  link:RelNotes/2.6.6.txt[2.6.6],
-  link:RelNotes/2.6.5.txt[2.6.5],
-  link:RelNotes/2.6.4.txt[2.6.4],
-  link:RelNotes/2.6.3.txt[2.6.3],
-  link:RelNotes/2.6.2.txt[2.6.2],
-  link:RelNotes/2.6.1.txt[2.6.1],
-  link:RelNotes/2.6.0.txt[2.6].
-
-* link:v2.5.6/git.html[documentation for release 2.5.6]
-
-* release notes for
-  link:RelNotes/2.5.6.txt[2.5.6],
-  link:RelNotes/2.5.5.txt[2.5.5],
-  link:RelNotes/2.5.4.txt[2.5.4],
-  link:RelNotes/2.5.3.txt[2.5.3],
-  link:RelNotes/2.5.2.txt[2.5.2],
-  link:RelNotes/2.5.1.txt[2.5.1],
-  link:RelNotes/2.5.0.txt[2.5].
-
-* link:v2.4.12/git.html[documentation for release 2.4.12]
-
-* release notes for
-  link:RelNotes/2.4.12.txt[2.4.12],
-  link:RelNotes/2.4.11.txt[2.4.11],
-  link:RelNotes/2.4.10.txt[2.4.10],
-  link:RelNotes/2.4.9.txt[2.4.9],
-  link:RelNotes/2.4.8.txt[2.4.8],
-  link:RelNotes/2.4.7.txt[2.4.7],
-  link:RelNotes/2.4.6.txt[2.4.6],
-  link:RelNotes/2.4.5.txt[2.4.5],
-  link:RelNotes/2.4.4.txt[2.4.4],
-  link:RelNotes/2.4.3.txt[2.4.3],
-  link:RelNotes/2.4.2.txt[2.4.2],
-  link:RelNotes/2.4.1.txt[2.4.1],
-  link:RelNotes/2.4.0.txt[2.4].
-
-* link:v2.3.10/git.html[documentation for release 2.3.10]
-
-* release notes for
-  link:RelNotes/2.3.10.txt[2.3.10],
-  link:RelNotes/2.3.9.txt[2.3.9],
-  link:RelNotes/2.3.8.txt[2.3.8],
-  link:RelNotes/2.3.7.txt[2.3.7],
-  link:RelNotes/2.3.6.txt[2.3.6],
-  link:RelNotes/2.3.5.txt[2.3.5],
-  link:RelNotes/2.3.4.txt[2.3.4],
-  link:RelNotes/2.3.3.txt[2.3.3],
-  link:RelNotes/2.3.2.txt[2.3.2],
-  link:RelNotes/2.3.1.txt[2.3.1],
-  link:RelNotes/2.3.0.txt[2.3].
-
-* link:v2.2.3/git.html[documentation for release 2.2.3]
-
-* release notes for
-  link:RelNotes/2.2.3.txt[2.2.3],
-  link:RelNotes/2.2.2.txt[2.2.2],
-  link:RelNotes/2.2.1.txt[2.2.1],
-  link:RelNotes/2.2.0.txt[2.2].
-
-* link:v2.1.4/git.html[documentation for release 2.1.4]
-
-* release notes for
-  link:RelNotes/2.1.4.txt[2.1.4],
-  link:RelNotes/2.1.3.txt[2.1.3],
-  link:RelNotes/2.1.2.txt[2.1.2],
-  link:RelNotes/2.1.1.txt[2.1.1],
-  link:RelNotes/2.1.0.txt[2.1].
-
-* link:v2.0.5/git.html[documentation for release 2.0.5]
-
-* release notes for
-  link:RelNotes/2.0.5.txt[2.0.5],
-  link:RelNotes/2.0.4.txt[2.0.4],
-  link:RelNotes/2.0.3.txt[2.0.3],
-  link:RelNotes/2.0.2.txt[2.0.2],
-  link:RelNotes/2.0.1.txt[2.0.1],
-  link:RelNotes/2.0.0.txt[2.0.0].
-
-* link:v1.9.5/git.html[documentation for release 1.9.5]
-
-* release notes for
-  link:RelNotes/1.9.5.txt[1.9.5],
-  link:RelNotes/1.9.4.txt[1.9.4],
-  link:RelNotes/1.9.3.txt[1.9.3],
-  link:RelNotes/1.9.2.txt[1.9.2],
-  link:RelNotes/1.9.1.txt[1.9.1],
-  link:RelNotes/1.9.0.txt[1.9.0].
-
-* link:v1.8.5.6/git.html[documentation for release 1.8.5.6]
-
-* release notes for
-  link:RelNotes/1.8.5.6.txt[1.8.5.6],
-  link:RelNotes/1.8.5.5.txt[1.8.5.5],
-  link:RelNotes/1.8.5.4.txt[1.8.5.4],
-  link:RelNotes/1.8.5.3.txt[1.8.5.3],
-  link:RelNotes/1.8.5.2.txt[1.8.5.2],
-  link:RelNotes/1.8.5.1.txt[1.8.5.1],
-  link:RelNotes/1.8.5.txt[1.8.5].
-
-* link:v1.8.4.5/git.html[documentation for release 1.8.4.5]
-
-* release notes for
-  link:RelNotes/1.8.4.5.txt[1.8.4.5],
-  link:RelNotes/1.8.4.4.txt[1.8.4.4],
-  link:RelNotes/1.8.4.3.txt[1.8.4.3],
-  link:RelNotes/1.8.4.2.txt[1.8.4.2],
-  link:RelNotes/1.8.4.1.txt[1.8.4.1],
-  link:RelNotes/1.8.4.txt[1.8.4].
-
-* link:v1.8.3.4/git.html[documentation for release 1.8.3.4]
-
-* release notes for
-  link:RelNotes/1.8.3.4.txt[1.8.3.4],
-  link:RelNotes/1.8.3.3.txt[1.8.3.3],
-  link:RelNotes/1.8.3.2.txt[1.8.3.2],
-  link:RelNotes/1.8.3.1.txt[1.8.3.1],
-  link:RelNotes/1.8.3.txt[1.8.3].
-
-* link:v1.8.2.3/git.html[documentation for release 1.8.2.3]
-
-* release notes for
-  link:RelNotes/1.8.2.3.txt[1.8.2.3],
-  link:RelNotes/1.8.2.2.txt[1.8.2.2],
-  link:RelNotes/1.8.2.1.txt[1.8.2.1],
-  link:RelNotes/1.8.2.txt[1.8.2].
-
-* link:v1.8.1.6/git.html[documentation for release 1.8.1.6]
-
-* release notes for
-  link:RelNotes/1.8.1.6.txt[1.8.1.6],
-  link:RelNotes/1.8.1.5.txt[1.8.1.5],
-  link:RelNotes/1.8.1.4.txt[1.8.1.4],
-  link:RelNotes/1.8.1.3.txt[1.8.1.3],
-  link:RelNotes/1.8.1.2.txt[1.8.1.2],
-  link:RelNotes/1.8.1.1.txt[1.8.1.1],
-  link:RelNotes/1.8.1.txt[1.8.1].
-
-* link:v1.8.0.3/git.html[documentation for release 1.8.0.3]
-
-* release notes for
-  link:RelNotes/1.8.0.3.txt[1.8.0.3],
-  link:RelNotes/1.8.0.2.txt[1.8.0.2],
-  link:RelNotes/1.8.0.1.txt[1.8.0.1],
-  link:RelNotes/1.8.0.txt[1.8.0].
-
-* link:v1.7.12.4/git.html[documentation for release 1.7.12.4]
-
-* release notes for
-  link:RelNotes/1.7.12.4.txt[1.7.12.4],
-  link:RelNotes/1.7.12.3.txt[1.7.12.3],
-  link:RelNotes/1.7.12.2.txt[1.7.12.2],
-  link:RelNotes/1.7.12.1.txt[1.7.12.1],
-  link:RelNotes/1.7.12.txt[1.7.12].
-
-* link:v1.7.11.7/git.html[documentation for release 1.7.11.7]
-
-* release notes for
-  link:RelNotes/1.7.11.7.txt[1.7.11.7],
-  link:RelNotes/1.7.11.6.txt[1.7.11.6],
-  link:RelNotes/1.7.11.5.txt[1.7.11.5],
-  link:RelNotes/1.7.11.4.txt[1.7.11.4],
-  link:RelNotes/1.7.11.3.txt[1.7.11.3],
-  link:RelNotes/1.7.11.2.txt[1.7.11.2],
-  link:RelNotes/1.7.11.1.txt[1.7.11.1],
-  link:RelNotes/1.7.11.txt[1.7.11].
-
-* link:v1.7.10.5/git.html[documentation for release 1.7.10.5]
-
-* release notes for
-  link:RelNotes/1.7.10.5.txt[1.7.10.5],
-  link:RelNotes/1.7.10.4.txt[1.7.10.4],
-  link:RelNotes/1.7.10.3.txt[1.7.10.3],
-  link:RelNotes/1.7.10.2.txt[1.7.10.2],
-  link:RelNotes/1.7.10.1.txt[1.7.10.1],
-  link:RelNotes/1.7.10.txt[1.7.10].
-
-* link:v1.7.9.7/git.html[documentation for release 1.7.9.7]
-
-* release notes for
-  link:RelNotes/1.7.9.7.txt[1.7.9.7],
-  link:RelNotes/1.7.9.6.txt[1.7.9.6],
-  link:RelNotes/1.7.9.5.txt[1.7.9.5],
-  link:RelNotes/1.7.9.4.txt[1.7.9.4],
-  link:RelNotes/1.7.9.3.txt[1.7.9.3],
-  link:RelNotes/1.7.9.2.txt[1.7.9.2],
-  link:RelNotes/1.7.9.1.txt[1.7.9.1],
-  link:RelNotes/1.7.9.txt[1.7.9].
-
-* link:v1.7.8.6/git.html[documentation for release 1.7.8.6]
-
-* release notes for
-  link:RelNotes/1.7.8.6.txt[1.7.8.6],
-  link:RelNotes/1.7.8.5.txt[1.7.8.5],
-  link:RelNotes/1.7.8.4.txt[1.7.8.4],
-  link:RelNotes/1.7.8.3.txt[1.7.8.3],
-  link:RelNotes/1.7.8.2.txt[1.7.8.2],
-  link:RelNotes/1.7.8.1.txt[1.7.8.1],
-  link:RelNotes/1.7.8.txt[1.7.8].
-
-* link:v1.7.7.7/git.html[documentation for release 1.7.7.7]
-
-* release notes for
-  link:RelNotes/1.7.7.7.txt[1.7.7.7],
-  link:RelNotes/1.7.7.6.txt[1.7.7.6],
-  link:RelNotes/1.7.7.5.txt[1.7.7.5],
-  link:RelNotes/1.7.7.4.txt[1.7.7.4],
-  link:RelNotes/1.7.7.3.txt[1.7.7.3],
-  link:RelNotes/1.7.7.2.txt[1.7.7.2],
-  link:RelNotes/1.7.7.1.txt[1.7.7.1],
-  link:RelNotes/1.7.7.txt[1.7.7].
-
-* link:v1.7.6.6/git.html[documentation for release 1.7.6.6]
-
-* release notes for
-  link:RelNotes/1.7.6.6.txt[1.7.6.6],
-  link:RelNotes/1.7.6.5.txt[1.7.6.5],
-  link:RelNotes/1.7.6.4.txt[1.7.6.4],
-  link:RelNotes/1.7.6.3.txt[1.7.6.3],
-  link:RelNotes/1.7.6.2.txt[1.7.6.2],
-  link:RelNotes/1.7.6.1.txt[1.7.6.1],
-  link:RelNotes/1.7.6.txt[1.7.6].
-
-* link:v1.7.5.4/git.html[documentation for release 1.7.5.4]
-
-* release notes for
-  link:RelNotes/1.7.5.4.txt[1.7.5.4],
-  link:RelNotes/1.7.5.3.txt[1.7.5.3],
-  link:RelNotes/1.7.5.2.txt[1.7.5.2],
-  link:RelNotes/1.7.5.1.txt[1.7.5.1],
-  link:RelNotes/1.7.5.txt[1.7.5].
-
-* link:v1.7.4.5/git.html[documentation for release 1.7.4.5]
-
-* release notes for
-  link:RelNotes/1.7.4.5.txt[1.7.4.5],
-  link:RelNotes/1.7.4.4.txt[1.7.4.4],
-  link:RelNotes/1.7.4.3.txt[1.7.4.3],
-  link:RelNotes/1.7.4.2.txt[1.7.4.2],
-  link:RelNotes/1.7.4.1.txt[1.7.4.1],
-  link:RelNotes/1.7.4.txt[1.7.4].
-
-* link:v1.7.3.5/git.html[documentation for release 1.7.3.5]
-
-* release notes for
-  link:RelNotes/1.7.3.5.txt[1.7.3.5],
-  link:RelNotes/1.7.3.4.txt[1.7.3.4],
-  link:RelNotes/1.7.3.3.txt[1.7.3.3],
-  link:RelNotes/1.7.3.2.txt[1.7.3.2],
-  link:RelNotes/1.7.3.1.txt[1.7.3.1],
-  link:RelNotes/1.7.3.txt[1.7.3].
-
-* link:v1.7.2.5/git.html[documentation for release 1.7.2.5]
-
-* release notes for
-  link:RelNotes/1.7.2.5.txt[1.7.2.5],
-  link:RelNotes/1.7.2.4.txt[1.7.2.4],
-  link:RelNotes/1.7.2.3.txt[1.7.2.3],
-  link:RelNotes/1.7.2.2.txt[1.7.2.2],
-  link:RelNotes/1.7.2.1.txt[1.7.2.1],
-  link:RelNotes/1.7.2.txt[1.7.2].
-
-* link:v1.7.1.4/git.html[documentation for release 1.7.1.4]
-
-* release notes for
-  link:RelNotes/1.7.1.4.txt[1.7.1.4],
-  link:RelNotes/1.7.1.3.txt[1.7.1.3],
-  link:RelNotes/1.7.1.2.txt[1.7.1.2],
-  link:RelNotes/1.7.1.1.txt[1.7.1.1],
-  link:RelNotes/1.7.1.txt[1.7.1].
-
-* link:v1.7.0.9/git.html[documentation for release 1.7.0.9]
-
-* release notes for
-  link:RelNotes/1.7.0.9.txt[1.7.0.9],
-  link:RelNotes/1.7.0.8.txt[1.7.0.8],
-  link:RelNotes/1.7.0.7.txt[1.7.0.7],
-  link:RelNotes/1.7.0.6.txt[1.7.0.6],
-  link:RelNotes/1.7.0.5.txt[1.7.0.5],
-  link:RelNotes/1.7.0.4.txt[1.7.0.4],
-  link:RelNotes/1.7.0.3.txt[1.7.0.3],
-  link:RelNotes/1.7.0.2.txt[1.7.0.2],
-  link:RelNotes/1.7.0.1.txt[1.7.0.1],
-  link:RelNotes/1.7.0.txt[1.7.0].
-
-* link:v1.6.6.3/git.html[documentation for release 1.6.6.3]
-
-* release notes for
-  link:RelNotes/1.6.6.3.txt[1.6.6.3],
-  link:RelNotes/1.6.6.2.txt[1.6.6.2],
-  link:RelNotes/1.6.6.1.txt[1.6.6.1],
-  link:RelNotes/1.6.6.txt[1.6.6].
-
-* link:v1.6.5.9/git.html[documentation for release 1.6.5.9]
-
-* release notes for
-  link:RelNotes/1.6.5.9.txt[1.6.5.9],
-  link:RelNotes/1.6.5.8.txt[1.6.5.8],
-  link:RelNotes/1.6.5.7.txt[1.6.5.7],
-  link:RelNotes/1.6.5.6.txt[1.6.5.6],
-  link:RelNotes/1.6.5.5.txt[1.6.5.5],
-  link:RelNotes/1.6.5.4.txt[1.6.5.4],
-  link:RelNotes/1.6.5.3.txt[1.6.5.3],
-  link:RelNotes/1.6.5.2.txt[1.6.5.2],
-  link:RelNotes/1.6.5.1.txt[1.6.5.1],
-  link:RelNotes/1.6.5.txt[1.6.5].
-
-* link:v1.6.4.5/git.html[documentation for release 1.6.4.5]
-
-* release notes for
-  link:RelNotes/1.6.4.5.txt[1.6.4.5],
-  link:RelNotes/1.6.4.4.txt[1.6.4.4],
-  link:RelNotes/1.6.4.3.txt[1.6.4.3],
-  link:RelNotes/1.6.4.2.txt[1.6.4.2],
-  link:RelNotes/1.6.4.1.txt[1.6.4.1],
-  link:RelNotes/1.6.4.txt[1.6.4].
-
-* link:v1.6.3.4/git.html[documentation for release 1.6.3.4]
-
-* release notes for
-  link:RelNotes/1.6.3.4.txt[1.6.3.4],
-  link:RelNotes/1.6.3.3.txt[1.6.3.3],
-  link:RelNotes/1.6.3.2.txt[1.6.3.2],
-  link:RelNotes/1.6.3.1.txt[1.6.3.1],
-  link:RelNotes/1.6.3.txt[1.6.3].
-
-* release notes for
-  link:RelNotes/1.6.2.5.txt[1.6.2.5],
-  link:RelNotes/1.6.2.4.txt[1.6.2.4],
-  link:RelNotes/1.6.2.3.txt[1.6.2.3],
-  link:RelNotes/1.6.2.2.txt[1.6.2.2],
-  link:RelNotes/1.6.2.1.txt[1.6.2.1],
-  link:RelNotes/1.6.2.txt[1.6.2].
-
-* link:v1.6.1.3/git.html[documentation for release 1.6.1.3]
-
-* release notes for
-  link:RelNotes/1.6.1.3.txt[1.6.1.3],
-  link:RelNotes/1.6.1.2.txt[1.6.1.2],
-  link:RelNotes/1.6.1.1.txt[1.6.1.1],
-  link:RelNotes/1.6.1.txt[1.6.1].
-
-* link:v1.6.0.6/git.html[documentation for release 1.6.0.6]
-
-* release notes for
-  link:RelNotes/1.6.0.6.txt[1.6.0.6],
-  link:RelNotes/1.6.0.5.txt[1.6.0.5],
-  link:RelNotes/1.6.0.4.txt[1.6.0.4],
-  link:RelNotes/1.6.0.3.txt[1.6.0.3],
-  link:RelNotes/1.6.0.2.txt[1.6.0.2],
-  link:RelNotes/1.6.0.1.txt[1.6.0.1],
-  link:RelNotes/1.6.0.txt[1.6.0].
-
-* link:v1.5.6.6/git.html[documentation for release 1.5.6.6]
-
-* release notes for
-  link:RelNotes/1.5.6.6.txt[1.5.6.6],
-  link:RelNotes/1.5.6.5.txt[1.5.6.5],
-  link:RelNotes/1.5.6.4.txt[1.5.6.4],
-  link:RelNotes/1.5.6.3.txt[1.5.6.3],
-  link:RelNotes/1.5.6.2.txt[1.5.6.2],
-  link:RelNotes/1.5.6.1.txt[1.5.6.1],
-  link:RelNotes/1.5.6.txt[1.5.6].
-
-* link:v1.5.5.6/git.html[documentation for release 1.5.5.6]
-
-* release notes for
-  link:RelNotes/1.5.5.6.txt[1.5.5.6],
-  link:RelNotes/1.5.5.5.txt[1.5.5.5],
-  link:RelNotes/1.5.5.4.txt[1.5.5.4],
-  link:RelNotes/1.5.5.3.txt[1.5.5.3],
-  link:RelNotes/1.5.5.2.txt[1.5.5.2],
-  link:RelNotes/1.5.5.1.txt[1.5.5.1],
-  link:RelNotes/1.5.5.txt[1.5.5].
-
-* link:v1.5.4.7/git.html[documentation for release 1.5.4.7]
-
-* release notes for
-  link:RelNotes/1.5.4.7.txt[1.5.4.7],
-  link:RelNotes/1.5.4.6.txt[1.5.4.6],
-  link:RelNotes/1.5.4.5.txt[1.5.4.5],
-  link:RelNotes/1.5.4.4.txt[1.5.4.4],
-  link:RelNotes/1.5.4.3.txt[1.5.4.3],
-  link:RelNotes/1.5.4.2.txt[1.5.4.2],
-  link:RelNotes/1.5.4.1.txt[1.5.4.1],
-  link:RelNotes/1.5.4.txt[1.5.4].
-
-* link:v1.5.3.8/git.html[documentation for release 1.5.3.8]
-
-* release notes for
-  link:RelNotes/1.5.3.8.txt[1.5.3.8],
-  link:RelNotes/1.5.3.7.txt[1.5.3.7],
-  link:RelNotes/1.5.3.6.txt[1.5.3.6],
-  link:RelNotes/1.5.3.5.txt[1.5.3.5],
-  link:RelNotes/1.5.3.4.txt[1.5.3.4],
-  link:RelNotes/1.5.3.3.txt[1.5.3.3],
-  link:RelNotes/1.5.3.2.txt[1.5.3.2],
-  link:RelNotes/1.5.3.1.txt[1.5.3.1],
-  link:RelNotes/1.5.3.txt[1.5.3].
-
-* link:v1.5.2.5/git.html[documentation for release 1.5.2.5]
-
-* release notes for
-  link:RelNotes/1.5.2.5.txt[1.5.2.5],
-  link:RelNotes/1.5.2.4.txt[1.5.2.4],
-  link:RelNotes/1.5.2.3.txt[1.5.2.3],
-  link:RelNotes/1.5.2.2.txt[1.5.2.2],
-  link:RelNotes/1.5.2.1.txt[1.5.2.1],
-  link:RelNotes/1.5.2.txt[1.5.2].
-
-* link:v1.5.1.6/git.html[documentation for release 1.5.1.6]
-
-* release notes for
-  link:RelNotes/1.5.1.6.txt[1.5.1.6],
-  link:RelNotes/1.5.1.5.txt[1.5.1.5],
-  link:RelNotes/1.5.1.4.txt[1.5.1.4],
-  link:RelNotes/1.5.1.3.txt[1.5.1.3],
-  link:RelNotes/1.5.1.2.txt[1.5.1.2],
-  link:RelNotes/1.5.1.1.txt[1.5.1.1],
-  link:RelNotes/1.5.1.txt[1.5.1].
-
-* link:v1.5.0.7/git.html[documentation for release 1.5.0.7]
-
-* release notes for
-  link:RelNotes/1.5.0.7.txt[1.5.0.7],
-  link:RelNotes/1.5.0.6.txt[1.5.0.6],
-  link:RelNotes/1.5.0.5.txt[1.5.0.5],
-  link:RelNotes/1.5.0.3.txt[1.5.0.3],
-  link:RelNotes/1.5.0.2.txt[1.5.0.2],
-  link:RelNotes/1.5.0.1.txt[1.5.0.1],
-  link:RelNotes/1.5.0.txt[1.5.0].
-
-* documentation for release link:v1.4.4.4/git.html[1.4.4.4],
-  link:v1.3.3/git.html[1.3.3],
-  link:v1.2.6/git.html[1.2.6],
-  link:v1.0.13/git.html[1.0.13].
-
-============
-
-endif::stalenotes[]
 
 OPTIONS
 -------
index f3a75d1ce1c5a2709b452faca76f5f6f1cdb7de0..f970196bc1c52f489538ab515e1640ac435fead3 100644 (file)
@@ -101,16 +101,6 @@ $ git help credential-foo
 $ git config --global credential.helper foo
 -------------------------------------------
 
-If there are multiple instances of the `credential.helper` configuration
-variable, each helper will be tried in turn, and may provide a username,
-password, or nothing. Once Git has acquired both a username and a
-password, no more helpers will be tried.
-
-If `credential.helper` is configured to the empty string, this resets
-the helper list to empty (so you may override a helper set by a
-lower-priority config file by configuring the empty-string helper,
-followed by whatever set of helpers you would like).
-
 
 CREDENTIAL CONTEXTS
 -------------------
@@ -162,6 +152,16 @@ helper::
        shell (so, for example, setting this to `foo --option=bar` will execute
        `git credential-foo --option=bar` via the shell. See the manual of
        specific helpers for examples of their use.
++
+If there are multiple instances of the `credential.helper` configuration
+variable, each helper will be tried in turn, and may provide a username,
+password, or nothing. Once Git has acquired both a username and a
+password, no more helpers will be tried.
++
+If `credential.helper` is configured to the empty string, this resets
+the helper list to empty (so you may override a helper set by a
+lower-priority config file by configuring the empty-string helper,
+followed by whatever set of helpers you would like).
 
 username::
 
index 706091a569c17cae93e250ed7cb34d056a5b25f0..b2514f4d4426a730e65bcae264a6364eb50bcd60 100644 (file)
@@ -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
 ---
index 96156e5e1f5fe86cd793ad2a89b417f2bf6a2245..88450589aff7e656a5fbf52a36da433d1627d177 100644 (file)
@@ -84,7 +84,7 @@ separator (rules for Perl's "`split(" ", $line)`").
 
 * Fields use modified URI encoding, defined in RFC 3986, section 2.1
 (Percent-Encoding), or rather "Query string encoding" (see
-http://en.wikipedia.org/wiki/Query_string#URL_encoding[]), the difference
+https://en.wikipedia.org/wiki/Query_string#URL_encoding[]), the difference
 being that SP (" ") can be encoded as "{plus}" (and therefore "{plus}" has to be
 also percent-encoded).
 +
index 47b286b33e4edd937fe6f7cbe727713e0e61e324..38040e95b5cdb137fd3575076e5f69d393c19280 100644 (file)
@@ -199,7 +199,7 @@ endif::git-rev-list[]
   than given and there are spaces on its left, use those spaces
 - '%><(<N>)', '%><|(<N>)': similar to '% <(<N>)', '%<|(<N>)'
   respectively, but padding both sides (i.e. the text is centered)
--%(trailers): display the trailers of the body as interpreted by
+- %(trailers): display the trailers of the body as interpreted by
   linkgit:git-interpret-trailers[1]
 
 NOTE: Some placeholders may depend on other options given to the
index a02f7324c01ee5811441d2a6dcb93aafd892298c..a46f70c2b16c2c03f47ccb3444cffb491b68ed83 100644 (file)
@@ -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.
index 7f8e78d916c0b38ed6757aee6578a53cab30d9d0..6c77b4920c92a0b327fb88d2b461ec0d918ec99a 100644 (file)
@@ -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
index 36768b479e16c9456982230f078d98513a72c37f..829b5581105468d4d3b457e60b10cdbdf317fbbc 100644 (file)
@@ -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 (file)
index 0000000..793508c
--- /dev/null
@@ -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.
index 5b0ba3ef20144d1676cedbd8435e7bffddcde2f7..a34917153fd3dc10fefe71a1ba05e5dacb9af7f9 100644 (file)
@@ -473,13 +473,10 @@ that it wants to update, it sends a line listing the obj-id currently on
 the server, the obj-id the client would like to update it to and the name
 of the reference.
 
-This list is followed by a flush-pkt. Then the push options are transmitted
-one per packet followed by another flush-pkt. After that the packfile that
-should contain all the objects that the server will need to complete the new
-references will be sent.
+This list is followed by a flush-pkt.
 
 ----
-  update-request    =  *shallow ( command-list | push-cert ) [packfile]
+  update-requests   =  *shallow ( command-list | push-cert )
 
   shallow           =  PKT-LINE("shallow" SP obj-id)
 
@@ -500,12 +497,35 @@ references will be sent.
                      PKT-LINE("pusher" SP ident LF)
                      PKT-LINE("pushee" SP url LF)
                      PKT-LINE("nonce" SP nonce LF)
+                     *PKT-LINE("push-option" SP push-option LF)
                      PKT-LINE(LF)
                      *PKT-LINE(command LF)
                      *PKT-LINE(gpg-signature-lines LF)
                      PKT-LINE("push-cert-end" LF)
 
-  packfile          = "PACK" 28*(OCTET)
+  push-option       =  1*( VCHAR | SP )
+----
+
+If the server has advertised the 'push-options' capability and the client has
+specified 'push-options' as part of the capability list above, the client then
+sends its push options followed by a flush-pkt.
+
+----
+  push-options      =  *PKT-LINE(push-option) flush-pkt
+----
+
+For backwards compatibility with older Git servers, if the client sends a push
+cert and push options, it MUST send its push options both embedded within the
+push cert and after the push cert. (Note that the push options within the cert
+are prefixed, but the push options after the cert are not.) Both these lists
+MUST be the same, modulo the prefix.
+
+After that the packfile that
+should contain all the objects that the server will need to complete the new
+references will be sent.
+
+----
+  packfile          =  "PACK" 28*(OCTET)
 ----
 
 If the receiving end does not support delete-refs, the sending end MUST
index afcf9e9abfe72afb1f2727f5f0fd9a796db0406f..4f94fc757459ab40a312144aeaa5648142cbc3a6 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.13.0
+DEF_VER=v2.13.GIT
 
 LF='
 '
index e35542e63181144397053bf7932fad2cf97027bb..7c621f7f76181eb3d255e8d6eaf28e6e63dbb92f 100644 (file)
--- 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)
@@ -1414,7 +1418,14 @@ else
        DC_SHA1 := YesPlease
        LIB_OBJS += sha1dc/sha1.o
        LIB_OBJS += sha1dc/ubc_check.o
-       BASIC_CFLAGS += -DSHA1_DC
+       BASIC_CFLAGS += \
+               -DSHA1_DC \
+               -DSHA1DC_NO_STANDARD_INCLUDES \
+               -DSHA1DC_INIT_SAFE_HASH_DEFAULT=0 \
+               -DSHA1DC_CUSTOM_INCLUDE_SHA1_C="\"cache.h\"" \
+               -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C="\"sha1dc_git.c\"" \
+               -DSHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H="\"sha1dc_git.h\"" \
+               -DSHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C="\"git-compat-util.h\""
 endif
 endif
 endif
@@ -2238,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)))'\' >>$@+
@@ -2270,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
index 125bf78f3b9ed2f1444e1873ed02cce9f0f4c5b8..983f6689bd29f83fa3514e74362858daaf879cce 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.13.0.txt
\ No newline at end of file
+Documentation/RelNotes/2.14.0.txt
\ No newline at end of file
diff --git a/apply.c b/apply.c
index e6dbab26ad54b9a3af06f6adad928c6e1526db58..854faa67795bcd356b33420f46260f208c2ec047 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -3741,7 +3741,7 @@ static int check_to_create(struct apply_state *state,
                        return 0;
 
                return EXISTS_IN_WORKTREE;
-       } else if ((errno != ENOENT) && (errno != ENOTDIR)) {
+       } else if (!is_missing_file_error(errno)) {
                return error_errno("%s", new_name);
        }
        return 0;
@@ -4091,181 +4091,181 @@ static int build_fake_ancestor(struct apply_state *state, struct patch *list)
        res = write_locked_index(&result, &lock, COMMIT_LOCK);
        discard_index(&result);
 
-        if (res)
-                return error(_("could not write temporary index to %s"),
-                             state->fake_ancestor);
+       if (res)
+               return error(_("could not write temporary index to %s"),
+                            state->fake_ancestor);
 
-        return 0;
- }
+       return 0;
+}
 
- static void stat_patch_list(struct apply_state *state, struct patch *patch)
- {
-        int files, adds, dels;
+static void stat_patch_list(struct apply_state *state, struct patch *patch)
+{
+       int files, adds, dels;
 
-        for (files = adds = dels = 0 ; patch ; patch = patch->next) {
-                files++;
-                adds += patch->lines_added;
-                dels += patch->lines_deleted;
-                show_stats(state, patch);
-        }
+       for (files = adds = dels = 0 ; patch ; patch = patch->next) {
+               files++;
+               adds += patch->lines_added;
+               dels += patch->lines_deleted;
+               show_stats(state, patch);
+       }
 
-        print_stat_summary(stdout, files, adds, dels);
- }
+       print_stat_summary(stdout, files, adds, dels);
+}
 
- static void numstat_patch_list(struct apply_state *state,
-                               struct patch *patch)
- {
-        for ( ; patch; patch = patch->next) {
-                const char *name;
-                name = patch->new_name ? patch->new_name : patch->old_name;
-                if (patch->is_binary)
-                        printf("-\t-\t");
-                else
-                        printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
-                write_name_quoted(name, stdout, state->line_termination);
-        }
- }
-
- static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
- {
-        if (mode)
-                printf(" %s mode %06o %s\n", newdelete, mode, name);
-        else
-                printf(" %s %s\n", newdelete, name);
- }
-
- static void show_mode_change(struct patch *p, int show_name)
- {
-        if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
-                if (show_name)
-                        printf(" mode change %06o => %06o %s\n",
-                               p->old_mode, p->new_mode, p->new_name);
-                else
-                        printf(" mode change %06o => %06o\n",
-                               p->old_mode, p->new_mode);
-        }
- }
-
- static void show_rename_copy(struct patch *p)
- {
-        const char *renamecopy = p->is_rename ? "rename" : "copy";
-        const char *old, *new;
-
-        /* Find common prefix */
-        old = p->old_name;
-        new = p->new_name;
-        while (1) {
-                const char *slash_old, *slash_new;
-                slash_old = strchr(old, '/');
-                slash_new = strchr(new, '/');
-                if (!slash_old ||
-                    !slash_new ||
-                    slash_old - old != slash_new - new ||
-                    memcmp(old, new, slash_new - new))
-                        break;
-                old = slash_old + 1;
-                new = slash_new + 1;
-        }
-        /* p->old_name thru old is the common prefix, and old and new
-         * through the end of names are renames
-         */
-        if (old != p->old_name)
-                printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
-                       (int)(old - p->old_name), p->old_name,
-                       old, new, p->score);
-        else
-                printf(" %s %s => %s (%d%%)\n", renamecopy,
-                       p->old_name, p->new_name, p->score);
-        show_mode_change(p, 0);
- }
-
- static void summary_patch_list(struct patch *patch)
- {
-        struct patch *p;
-
-        for (p = patch; p; p = p->next) {
-                if (p->is_new)
-                        show_file_mode_name("create", p->new_mode, p->new_name);
-                else if (p->is_delete)
-                        show_file_mode_name("delete", p->old_mode, p->old_name);
-                else {
-                        if (p->is_rename || p->is_copy)
-                                show_rename_copy(p);
-                        else {
-                                if (p->score) {
-                                        printf(" rewrite %s (%d%%)\n",
-                                               p->new_name, p->score);
-                                        show_mode_change(p, 0);
-                                }
-                                else
-                                        show_mode_change(p, 1);
-                        }
-                }
-        }
- }
-
- static void patch_stats(struct apply_state *state, struct patch *patch)
- {
-        int lines = patch->lines_added + patch->lines_deleted;
-
-        if (lines > state->max_change)
-                state->max_change = lines;
-        if (patch->old_name) {
-                int len = quote_c_style(patch->old_name, NULL, NULL, 0);
-                if (!len)
-                        len = strlen(patch->old_name);
-                if (len > state->max_len)
-                        state->max_len = len;
-        }
-        if (patch->new_name) {
-                int len = quote_c_style(patch->new_name, NULL, NULL, 0);
-                if (!len)
-                        len = strlen(patch->new_name);
-                if (len > state->max_len)
-                        state->max_len = len;
-        }
- }
-
- static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
- {
-        if (state->update_index) {
-                if (remove_file_from_cache(patch->old_name) < 0)
-                        return error(_("unable to remove %s from index"), patch->old_name);
-        }
-        if (!state->cached) {
-                if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
-                        remove_path(patch->old_name);
-                }
-        }
-        return 0;
- }
-
- static int add_index_file(struct apply_state *state,
-                          const char *path,
-                          unsigned mode,
-                          void *buf,
-                          unsigned long size)
- {
-        struct stat st;
-        struct cache_entry *ce;
-        int namelen = strlen(path);
-        unsigned ce_size = cache_entry_size(namelen);
-
-        if (!state->update_index)
-                return 0;
-
-        ce = xcalloc(1, ce_size);
-        memcpy(ce->name, path, namelen);
-        ce->ce_mode = create_ce_mode(mode);
-        ce->ce_flags = create_ce_flags(0);
-        ce->ce_namelen = namelen;
-        if (S_ISGITLINK(mode)) {
-                const char *s;
-
-                if (!skip_prefix(buf, "Subproject commit ", &s) ||
-                    get_oid_hex(s, &ce->oid)) {
+static void numstat_patch_list(struct apply_state *state,
+                              struct patch *patch)
+{
+       for ( ; patch; patch = patch->next) {
+               const char *name;
+               name = patch->new_name ? patch->new_name : patch->old_name;
+               if (patch->is_binary)
+                       printf("-\t-\t");
+               else
+                       printf("%d\t%d\t", patch->lines_added, patch->lines_deleted);
+               write_name_quoted(name, stdout, state->line_termination);
+       }
+}
+
+static void show_file_mode_name(const char *newdelete, unsigned int mode, const char *name)
+{
+       if (mode)
+               printf(" %s mode %06o %s\n", newdelete, mode, name);
+       else
+               printf(" %s %s\n", newdelete, name);
+}
+
+static void show_mode_change(struct patch *p, int show_name)
+{
+       if (p->old_mode && p->new_mode && p->old_mode != p->new_mode) {
+               if (show_name)
+                       printf(" mode change %06o => %06o %s\n",
+                              p->old_mode, p->new_mode, p->new_name);
+               else
+                       printf(" mode change %06o => %06o\n",
+                              p->old_mode, p->new_mode);
+       }
+}
+
+static void show_rename_copy(struct patch *p)
+{
+       const char *renamecopy = p->is_rename ? "rename" : "copy";
+       const char *old, *new;
+
+       /* Find common prefix */
+       old = p->old_name;
+       new = p->new_name;
+       while (1) {
+               const char *slash_old, *slash_new;
+               slash_old = strchr(old, '/');
+               slash_new = strchr(new, '/');
+               if (!slash_old ||
+                   !slash_new ||
+                   slash_old - old != slash_new - new ||
+                   memcmp(old, new, slash_new - new))
+                       break;
+               old = slash_old + 1;
+               new = slash_new + 1;
+       }
+       /* p->old_name thru old is the common prefix, and old and new
+        * through the end of names are renames
+        */
+       if (old != p->old_name)
+               printf(" %s %.*s{%s => %s} (%d%%)\n", renamecopy,
+                      (int)(old - p->old_name), p->old_name,
+                      old, new, p->score);
+       else
+               printf(" %s %s => %s (%d%%)\n", renamecopy,
+                      p->old_name, p->new_name, p->score);
+       show_mode_change(p, 0);
+}
+
+static void summary_patch_list(struct patch *patch)
+{
+       struct patch *p;
+
+       for (p = patch; p; p = p->next) {
+               if (p->is_new)
+                       show_file_mode_name("create", p->new_mode, p->new_name);
+               else if (p->is_delete)
+                       show_file_mode_name("delete", p->old_mode, p->old_name);
+               else {
+                       if (p->is_rename || p->is_copy)
+                               show_rename_copy(p);
+                       else {
+                               if (p->score) {
+                                       printf(" rewrite %s (%d%%)\n",
+                                              p->new_name, p->score);
+                                       show_mode_change(p, 0);
+                               }
+                               else
+                                       show_mode_change(p, 1);
+                       }
+               }
+       }
+}
+
+static void patch_stats(struct apply_state *state, struct patch *patch)
+{
+       int lines = patch->lines_added + patch->lines_deleted;
+
+       if (lines > state->max_change)
+               state->max_change = lines;
+       if (patch->old_name) {
+               int len = quote_c_style(patch->old_name, NULL, NULL, 0);
+               if (!len)
+                       len = strlen(patch->old_name);
+               if (len > state->max_len)
+                       state->max_len = len;
+       }
+       if (patch->new_name) {
+               int len = quote_c_style(patch->new_name, NULL, NULL, 0);
+               if (!len)
+                       len = strlen(patch->new_name);
+               if (len > state->max_len)
+                       state->max_len = len;
+       }
+}
+
+static int remove_file(struct apply_state *state, struct patch *patch, int rmdir_empty)
+{
+       if (state->update_index) {
+               if (remove_file_from_cache(patch->old_name) < 0)
+                       return error(_("unable to remove %s from index"), patch->old_name);
+       }
+       if (!state->cached) {
+               if (!remove_or_warn(patch->old_mode, patch->old_name) && rmdir_empty) {
+                       remove_path(patch->old_name);
+               }
+       }
+       return 0;
+}
+
+static int add_index_file(struct apply_state *state,
+                         const char *path,
+                         unsigned mode,
+                         void *buf,
+                         unsigned long size)
+{
+       struct stat st;
+       struct cache_entry *ce;
+       int namelen = strlen(path);
+       unsigned ce_size = cache_entry_size(namelen);
+
+       if (!state->update_index)
+               return 0;
+
+       ce = xcalloc(1, ce_size);
+       memcpy(ce->name, path, namelen);
+       ce->ce_mode = create_ce_mode(mode);
+       ce->ce_flags = create_ce_flags(0);
+       ce->ce_namelen = namelen;
+       if (S_ISGITLINK(mode)) {
+               const char *s;
+
+               if (!skip_prefix(buf, "Subproject commit ", &s) ||
+                   get_oid_hex(s, &ce->oid)) {
                        free(ce);
-                       return error(_("corrupt patch for submodule %s"), path);
+                      return error(_("corrupt patch for submodule %s"), path);
                }
        } else {
                if (!state->cached) {
index 380e3aedd23c02da9ba70d9c5a258550a9637c71..073e60ebd3c366b42298ae443eee230407d4e3a7 100644 (file)
@@ -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 */
index b429a8d974a02b06c9c6bc46850f4c9fae4a6d01..27563e9e2602108997033e6fa79c660bc08aa863 100644 (file)
 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;
 }
index 60b889198656f42a1d073503c4da5886bf0a0526..b15a922dab56a525ca3ce7f1143d215fe39f3132 100644 (file)
--- 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;
index 415e0152e2cf42827b1b370d2e5c78f6e91bd961..62d1d82c1af0fa3bf77b32d63e9b4866f3428898 100644 (file)
--- 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;
index 08c9fb7266c3b2ff8b970c21d350be4adfe7b0fc..c952df692bef9ba1638e55448050db33ba76d399 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -546,7 +546,7 @@ static unsigned get_prn(unsigned count) {
 
 /*
  * Custom integer square root from
- * http://en.wikipedia.org/wiki/Integer_square_root
+ * https://en.wikipedia.org/wiki/Integer_square_root
  */
 static int sqrti(int val)
 {
@@ -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;
@@ -995,8 +995,10 @@ int bisect_next_all(const char *prefix, int no_checkout)
 
        steps_msg = xstrfmt(Q_("(roughly %d step)", "(roughly %d steps)",
                  steps), steps);
-       /* TRANSLATORS: the last %s will be replaced with
-          "(roughly %d steps)" translation */
+       /*
+        * TRANSLATORS: the last %s will be replaced with "(roughly %d
+        * steps)" translation.
+        */
        printf(Q_("Bisecting: %d revision left to test after this %s\n",
                  "Bisecting: %d revisions left to test after this %s\n",
                  nr), nr, steps_msg);
diff --git a/blame.c b/blame.c
new file mode 100644 (file)
index 0000000..843c845
--- /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 (file)
index 0000000..a6c915c
--- /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 1fcb8e44b00f3b9eedbe92da8abe47c2bebca7c7..fa2ab4f7a74e501366e1d3ceca9285ae60606165 100644 (file)
--- 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 59b394eea38494d5dfa525e28ca949e5a03efcf5..446061683101d5578f8de2b8aa43e4b6c51d1d66 100644 (file)
--- 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);
 
index ad5a2299ba2600e67c8aaa7719a2c7d54898958f..985316eb76505a60bca0aa303322bef35e7defaa 100644 (file)
--- 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);
index 9e4a89816d5fbde0ed0b3bebff8f90f70dd4480d..498ac80d07bc5b00a9c66e9e62449d7700bc7550 100644 (file)
--- 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);
index 9f53f020d0fc7184ac803a4346bdf426a814ed72..d9a2491e48f16d9ef7de5c05008f8c7e6a5e64b2 100644 (file)
@@ -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);
index a95dd8b4e6c793d9e51658e2ccbe535cf5a5af45..5ee146bfb31df8e3d8edf356f4817e3f53f8ad3b 100644 (file)
@@ -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)
@@ -1312,7 +1312,7 @@ static int parse_mail(struct am_state *state, const char *mail)
        }
 
        if (is_empty_file(am_path(state, "patch"))) {
-               printf_ln(_("Patch is empty. Was it split wrong?"));
+               printf_ln(_("Patch is empty."));
                die_user_resolve(state);
        }
 
@@ -1351,19 +1351,16 @@ static int get_mail_commit_oid(struct object_id *commit_id, const char *mail)
        struct strbuf sb = STRBUF_INIT;
        FILE *fp = xfopen(mail, "r");
        const char *x;
+       int ret = 0;
 
-       if (strbuf_getline_lf(&sb, fp))
-               return -1;
-
-       if (!skip_prefix(sb.buf, "From ", &x))
-               return -1;
-
-       if (get_oid_hex(x, commit_id) < 0)
-               return -1;
+       if (strbuf_getline_lf(&sb, fp) ||
+           !skip_prefix(sb.buf, "From ", &x) ||
+           get_oid_hex(x, commit_id) < 0)
+               ret = -1;
 
        strbuf_release(&sb);
        fclose(fp);
-       return 0;
+       return ret;
 }
 
 /**
@@ -1372,40 +1369,33 @@ static int get_mail_commit_oid(struct object_id *commit_id, const char *mail)
  */
 static void get_commit_info(struct am_state *state, struct commit *commit)
 {
-       const char *buffer, *ident_line, *author_date, *msg;
+       const char *buffer, *ident_line, *msg;
        size_t ident_len;
-       struct ident_split ident_split;
-       struct strbuf sb = STRBUF_INIT;
+       struct ident_split id;
 
        buffer = logmsg_reencode(commit, NULL, get_commit_output_encoding());
 
        ident_line = find_commit_header(buffer, "author", &ident_len);
 
-       if (split_ident_line(&ident_split, ident_line, ident_len) < 0) {
-               strbuf_add(&sb, ident_line, ident_len);
-               die(_("invalid ident line: %s"), sb.buf);
-       }
+       if (split_ident_line(&id, ident_line, ident_len) < 0)
+               die(_("invalid ident line: %.*s"), (int)ident_len, ident_line);
 
        assert(!state->author_name);
-       if (ident_split.name_begin) {
-               strbuf_add(&sb, ident_split.name_begin,
-                       ident_split.name_end - ident_split.name_begin);
-               state->author_name = strbuf_detach(&sb, NULL);
-       } else
+       if (id.name_begin)
+               state->author_name =
+                       xmemdupz(id.name_begin, id.name_end - id.name_begin);
+       else
                state->author_name = xstrdup("");
 
        assert(!state->author_email);
-       if (ident_split.mail_begin) {
-               strbuf_add(&sb, ident_split.mail_begin,
-                       ident_split.mail_end - ident_split.mail_begin);
-               state->author_email = strbuf_detach(&sb, NULL);
-       } else
+       if (id.mail_begin)
+               state->author_email =
+                       xmemdupz(id.mail_begin, id.mail_end - id.mail_begin);
+       else
                state->author_email = xstrdup("");
 
-       author_date = show_ident_date(&ident_split, DATE_MODE(NORMAL));
-       strbuf_addstr(&sb, author_date);
        assert(!state->author_date);
-       state->author_date = strbuf_detach(&sb, NULL);
+       state->author_date = xstrdup(show_ident_date(&id, DATE_MODE(NORMAL)));
 
        assert(!state->msg);
        msg = strstr(buffer, "\n\n");
@@ -1413,6 +1403,7 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
                die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
        state->msg = xstrdup(msg + 2);
        state->msg_len = strlen(state->msg);
+       unuse_commit_buffer(commit, buffer);
 }
 
 /**
@@ -1453,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);
@@ -1488,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);
 
@@ -1618,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);
        }
@@ -1683,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"));
@@ -1940,7 +1931,8 @@ static void am_resolve(struct am_state *state)
 
        if (unmerged_cache()) {
                printf_ln(_("You still have unmerged paths in your index.\n"
-                       "Did you forget to use 'git add'?"));
+                       "You should 'git add' each file with resolved conflicts to mark them as such.\n"
+                       "You might run `git rm` on a file to accept \"deleted by them\" for it."));
                die_user_resolve(state);
        }
 
@@ -2045,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));
 
@@ -2061,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));
 
@@ -2156,7 +2148,7 @@ static void am_abort(struct am_state *state)
        am_rerere_clear();
 
        curr_branch = resolve_refdup("HEAD", 0, curr_head.hash, NULL);
-       has_curr_head = !is_null_oid(&curr_head);
+       has_curr_head = curr_branch && !is_null_oid(&curr_head);
        if (!has_curr_head)
                hashcpy(curr_head.hash, EMPTY_TREE_SHA1_BIN);
 
@@ -2319,6 +2311,9 @@ int cmd_am(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
 
+       if (argc == 2 && !strcmp(argv[1], "-h"))
+               usage_with_options(usage, options);
+
        git_config(git_am_config, NULL);
 
        am_state_init(&state);
index 07506a3e457d1540acb7700e4677d715d6842735..d7a2df3b47439c5564a716402e815e0da4f0d8a9 100644 (file)
@@ -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;
+static unsigned blame_move_score;
+static unsigned blame_copy_score;
 
-       /* 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);
-}
+/* Remember to update object flag allocation in object.h */
+#define METAINFO_SHOWN         (1u<<12)
+#define MORE_THAN_ONE_PATH     (1u<<13)
 
-struct blame_list {
-       struct blame_entry *ent;
-       struct blame_entry split[3];
+struct progress_info {
+       struct progress *progress;
+       int blamed_lines;
 };
 
-/*
- * 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)
+static const char *nth_line_cb(void *data, long lno)
 {
-       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 @@ static void pass_blame(struct scoreboard *sb, struct origin *origin, int opt)
 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;
@@ -2688,12 +782,15 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                blame_date_width = sizeof("2006-10-19");
                break;
        case DATE_RELATIVE:
-               /* TRANSLATORS: This string is used to tell us the maximum
-                  display width for a relative timestamp in "git blame"
-                  output.  For C locale, "4 years, 11 months ago", which
-                  takes 22 places, is the longest among various forms of
-                  relative timestamps, but your language may need more or
-                  fewer display columns. */
+               /*
+                * TRANSLATORS: This string is used to tell us the
+                * maximum display width for a relative timestamp in
+                * "git blame" output.  For C locale, "4 years, 11
+                * months ago", which takes 22 places, is the longest
+                * among various forms of relative timestamps, but
+                * your language may need more or fewer display
+                * columns.
+                */
                blame_date_width = utf8_strwidth(_("4 years, 11 months ago")) + 1; /* add the null */
                break;
        case DATE_NORMAL:
@@ -2709,11 +806,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                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
@@ -2766,94 +858,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
        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");
@@ -2882,22 +893,13 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
        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);
@@ -2905,21 +907,38 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        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);
@@ -2933,9 +952,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
        }
 
        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;
 }
index 48a513a84dbf6cf2516a10cbcced267369bc747c..83fcda43dceec0255c6164eb42a35d3d89011efb 100644 (file)
@@ -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"));
        }
index 1890d7a6390dcae719af33ce5cb59e74832a80e8..4bffd7a2d8eee2ea251afef70f63f646f184a339 100644 (file)
@@ -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)
@@ -165,6 +167,8 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name,
                die("git cat-file %s: bad file", obj_name);
 
        write_or_die(1, buf, size);
+       free(buf);
+       free(obj_context.path);
        return 0;
 }
 
index 1d73d3ca3d0509d71b927d685f3239cdab3cc885..c7b8c08897193e225d8da32f4d402def3f16fe50 100644 (file)
@@ -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);
index bfa5419f335dc1db98059869de24f152cffb9aa6..1624eed7e7625c18da7ffb58f1b9ae456c3dbd77 100644 (file)
 #include "submodule-config.h"
 #include "submodule.h"
 
-static int recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
-
 static const char * const checkout_usage[] = {
        N_("git checkout [<options>] <branch>"),
        N_("git checkout [<options>] [<branch>] -- <file>..."),
        NULL,
 };
 
-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;
-}
-
 struct checkout_opts {
        int patch_mode;
        int quiet;
@@ -235,22 +216,24 @@ static int checkout_merged(int pos, const struct checkout *state)
        /*
         * NEEDSWORK:
         * There is absolutely no reason to write this as a blob object
-        * and create a phony cache entry just to leak.  This hack is
-        * primarily to get to the write_entry() machinery that massages
-        * the contents to work-tree format and writes out which only
-        * allows it for a cache entry.  The code in write_entry() needs
-        * to be refactored to allow us to feed a <buffer, size, mode>
-        * instead of a cache entry.  Such a refactoring would help
-        * merge_recursive as well (it also writes the merge result to the
-        * object database even when it may contain conflicts).
+        * and create a phony cache entry.  This hack is primarily to get
+        * to the write_entry() machinery that massages the contents to
+        * work-tree format and writes out which only allows it for a
+        * cache entry.  The code in write_entry() needs to be refactored
+        * to allow us to feed a <buffer, size, mode> instead of a cache
+        * entry.  Such a refactoring would help merge_recursive as well
+        * (it also writes the merge result to the object database even
+        * when it may contain conflicts).
         */
        if (write_sha1_file(result_buf.ptr, result_buf.size,
                            blob_type, oid.hash))
                die(_("Unable to add merge result for '%s'"), path);
+       free(result_buf.ptr);
        ce = make_cache_entry(mode, oid.hash, path, 2, 0);
        if (!ce)
                die(_("make_cache_entry failed for path '%s'"), path);
        status = checkout_entry(ce, state, NULL);
+       free(ce);
        return status;
 }
 
@@ -393,7 +376,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;
@@ -527,10 +510,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);
@@ -721,7 +704,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;
 }
 
@@ -807,7 +790,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;
@@ -833,7 +816,8 @@ static int switch_branches(const struct checkout_opts *opts,
        int flag, writeout_error = 0;
        memset(&old, 0, sizeof(old));
        old.path = path_to_free = resolve_refdup("HEAD", 0, rev.hash, &flag);
-       old.commit = lookup_commit_reference_gently(rev.hash, 1);
+       if (old.path)
+               old.commit = lookup_commit_reference_gently(&rev, 1);
        if (!(flag & REF_ISSYMREF))
                old.path = NULL;
 
@@ -873,7 +857,7 @@ static int git_checkout_config(const char *var, const char *value, void *cb)
        }
 
        if (starts_with(var, "submodule."))
-               return parse_submodule_config_option(var, value);
+               return submodule_config(var, value, NULL);
 
        return git_xmerge_config(var, value, NULL);
 }
@@ -1047,10 +1031,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;
@@ -1181,9 +1165,9 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                                N_("second guess 'git checkout <no-such-branch>'")),
                OPT_BOOL(0, "ignore-other-worktrees", &opts.ignore_other_worktrees,
                         N_("do not check if another worktree is holding the given ref")),
-               { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+               { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
                            "checkout", "control recursive updating of submodules",
-                           PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
                OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
                OPT_END(),
        };
@@ -1214,12 +1198,6 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                git_xmerge_config("merge.conflictstyle", conflict_style, NULL);
        }
 
-       if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
-               git_config(submodule_config, NULL);
-               if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT)
-                       set_config_update_recurse_submodules(recurse_submodules);
-       }
-
        if ((!!opts.new_branch + !!opts.new_branch_force + !!opts.new_orphan_branch) > 1)
                die(_("-b, -B and --orphan are mutually exclusive"));
 
@@ -1286,9 +1264,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
                 * new_branch && argc > 1 will be caught later.
                 */
                if (opts.new_branch && argc == 1)
-                       die(_("Cannot update paths and switch to branch '%s' at the same time.\n"
-                             "Did you intend to checkout '%s' which can not be resolved as commit?"),
-                           opts.new_branch, argv[0]);
+                       die(_("'%s' is not a commit and a branch '%s' cannot be created from it"),
+                               argv[0], opts.new_branch);
 
                if (opts.force_detach)
                        die(_("git checkout: --detach does not take a path argument '%s'"),
index d861f836a29bf98d248f27632a8c47711a5da3a5..142bf668cffe814006fae791f1bdc6b20fe6f366 100644 (file)
@@ -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();
 
index de85b85254e49ba0211ea6476179fc6d4c774ca9..743f16ae2aad7d71789f2b38287aea13cf29fc26 100644 (file)
@@ -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)
@@ -773,7 +776,9 @@ static int checkout(int submodule_progress)
 
 static int write_one_config(const char *key, const char *value, void *data)
 {
-       return git_config_set_multivar_gently(key, value ? value : "true", "^$", 0);
+       return git_config_set_multivar_gently(key,
+                                             value ? value : "true",
+                                             CONFIG_REGEX_NONE, 0);
 }
 
 static void write_config(struct string_list *config)
@@ -1035,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();
 
index 605017261c38dea1f5ada18c7d5b12446d6ea44f..f39c2b27375fcd70f050e56174b0966053652eef 100644 (file)
@@ -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;
                }
 
index 1d805f5da8abd1cfafc41c5c47fd048b2549fbf4..da1ba4c86205ca55ae1880cb768b093b101bd85a 100644 (file)
@@ -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"));
        }
@@ -1735,7 +1739,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        if (verbose || /* Truncate the message just before the diff, if any. */
            cleanup_mode == CLEANUP_SCISSORS)
-               wt_status_truncate_message_at_cut_line(&sb);
+               strbuf_setlen(&sb, wt_status_locate_end(sb.buf, sb.len));
 
        if (cleanup_mode != CLEANUP_NONE)
                strbuf_stripspace(&sb, cleanup_mode == CLEANUP_ALL);
@@ -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"));
index 3a554ad50ca60cd327ce94433022f8d42f024f52..7f6c25d4d95b37f7785e0b6872d059095548c87f 100644 (file)
@@ -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;
index a5cd8c513f96baaaa0d9a310e2bf02b8db41199b..893c8789f4f563c58041040740384322e6ea205b 100644 (file)
@@ -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;
                }
index 15c61fd8d1ef891b013404ea5e7cbe42befac7bd..a572da9d5152ddf541f71e1aaf8abdb3c98f8ffe 100644 (file)
@@ -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);
 
index 1af373d0021f56f539a1d261e908a60d92ff5b4f..f084826a293f96870d0cf6be05e3e346672fc5dc 100644 (file)
@@ -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);
 
index 326f88b6576d6c02e9a0bcecb5a0b402bb257372..492245f4280cdda6a8d9baa85ed943f5b245bf62 100644 (file)
@@ -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;
@@ -128,9 +128,11 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
        }
 
        /*
-        * NOTE! We expect "a ^b" to be equal to "a..b", so we
-        * reverse the order of the objects if the second one
-        * is marked UNINTERESTING.
+        * NOTE!  We expect "a..b" to expand to "^a b" but it is
+        * perfectly valid for revision range parser to yield "b ^a",
+        * which means the same thing. If we get the latter, i.e. the
+        * second one is marked UNINTERESTING, we recover the original
+        * order the user gave, i.e. "a..b", by swapping the trees.
         */
        nr_sha1 = opt->pending.nr;
        switch (nr_sha1) {
index d184aafab9e353279125f9f7f07b63c3780bcba1..0c8f86e40da6537d57f5ed6ca0ecdd005e29c8f5 100644 (file)
 #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 {
index 1354d0e4625c855676e7812be92d7c77bdecb407..b9a892f2693efe01a08c958f7064f13ba6ec43aa 100644 (file)
@@ -226,6 +226,7 @@ static void changed_files(struct hashmap *result, const char *index_path,
                hashmap_entry_init(entry, strhash(buf.buf));
                hashmap_add(result, entry);
        }
+       fclose(fp);
        if (finish_command(&diff_files))
                die("diff-files did not exit properly");
        strbuf_release(&index_env);
@@ -439,8 +440,10 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                }
 
                if (lmode && status != 'C') {
-                       if (checkout_path(lmode, &loid, src_path, &lstate))
-                               return error("could not write '%s'", src_path);
+                       if (checkout_path(lmode, &loid, src_path, &lstate)) {
+                               ret = error("could not write '%s'", src_path);
+                               goto finish;
+                       }
                }
 
                if (rmode && !S_ISLNK(rmode)) {
@@ -456,9 +459,12 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                        hashmap_add(&working_tree_dups, entry);
 
                        if (!use_wt_file(workdir, dst_path, &roid)) {
-                               if (checkout_path(rmode, &roid, dst_path, &rstate))
-                                       return error("could not write '%s'",
-                                                    dst_path);
+                               if (checkout_path(rmode, &roid, dst_path,
+                                                 &rstate)) {
+                                       ret = error("could not write '%s'",
+                                                   dst_path);
+                                       goto finish;
+                               }
                        } else if (!is_null_oid(&roid)) {
                                /*
                                 * Changes in the working tree need special
@@ -473,10 +479,12 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                                                ADD_CACHE_JUST_APPEND);
 
                                add_path(&rdir, rdir_len, dst_path);
-                               if (ensure_leading_directories(rdir.buf))
-                                       return error("could not create "
-                                                    "directory for '%s'",
-                                                    dst_path);
+                               if (ensure_leading_directories(rdir.buf)) {
+                                       ret = error("could not create "
+                                                   "directory for '%s'",
+                                                   dst_path);
+                                       goto finish;
+                               }
                                add_path(&wtdir, wtdir_len, dst_path);
                                if (symlinks) {
                                        if (symlink(wtdir.buf, rdir.buf)) {
@@ -497,13 +505,15 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                }
        }
 
+       fclose(fp);
+       fp = NULL;
        if (finish_command(&child)) {
                ret = error("error occurred running diff --raw");
                goto finish;
        }
 
        if (!i)
-               return 0;
+               goto finish;
 
        /*
         * Changes to submodules require special treatment.This loop writes a
@@ -626,6 +636,9 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                exit_cleanup(tmpdir, rc);
 
 finish:
+       if (fp)
+               fclose(fp);
+
        free(lbase_dir);
        free(rbase_dir);
        strbuf_release(&ldir);
index e0220630d00cf2c069ce1a43e9511df57685acd2..24e29ad7eab5edc0964a51f32602b062daabb907 100644 (file)
@@ -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)
@@ -734,6 +734,7 @@ static void handle_tag(const char *name, struct tag *tag)
                             oid_to_hex(&tag->object.oid));
                case DROP:
                        /* Ignore this tag altogether */
+                       free(buf);
                        return;
                case REWRITE:
                        if (tagged->type != OBJ_COMMIT) {
@@ -765,6 +766,7 @@ static void handle_tag(const char *name, struct tag *tag)
               (int)(tagger_end - tagger), tagger,
               tagger == tagger_end ? "" : "\n",
               (int)message_size, (int)message_size, message ? message : "");
+       free(buf);
 }
 
 static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
@@ -777,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;
                }
@@ -938,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));
 
index 5f2c2ab23e4cde17747147f23dcedaebe66c74a3..100248c5afe3e1c16fa7fe79696200aeb5d1bde2 100644 (file)
@@ -73,6 +73,13 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
                fetch_prune_config = git_config_bool(k, v);
                return 0;
        }
+
+       if (!strcmp(k, "submodule.recurse")) {
+               int r = git_config_bool(k, v) ?
+                       RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
+               recurse_submodules = r;
+       }
+
        return git_default_config(k, v, cb);
 }
 
@@ -636,8 +643,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 +777,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 +948,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);
        }
 
index 6faa3c0d2424600a5e69dd20466aa763d95b586c..70137b0b7e56e6319872fddc3527fe1094009dee 100644 (file)
@@ -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;
index b5e13a45560f9338a65191c22d213f33052bf9b9..cb2ba6cd1be46635ca8416a7d3f2b006f964190b 100644 (file)
@@ -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;
index 91f7696a85ec974d0ff5a1591cc404ed99efb7d7..f484eda43ca06046924931d2055b50c2f89ea8d7 100644 (file)
@@ -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";
index 3ffb5b4e8176bbcd1ecf2e4ae2dc84aee968cd22..d188871556d814b1532f7118f1d76741850a370d 100644 (file)
@@ -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,8 +289,22 @@ 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
        }
 
+       if (!strcmp(var, "submodule.recurse"))
+               recurse_submodules = git_config_bool(var, value);
+
        return st;
 }
 
@@ -495,6 +509,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 +882,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 +1206,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 +1245,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
 
index 4ff567db475573f8f45830983b7d3716d6832b4b..04b9dcaf0f4ca90712cbb85cace521e226194f3b 100644 (file)
@@ -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');
        }
index b3b10cc1edba2f6d77fdf4b12c328a7968fb5499..e89ec941ce2c97de7f8233a5e7e6f9010fabdb64 100644 (file)
@@ -110,6 +110,8 @@ static void init_log_defaults(void)
 {
        init_grep_defaults();
        init_diff_ui_defaults();
+
+       decoration_style = auto_decoration_style();
 }
 
 static void cmd_log_init_defaults(struct rev_info *rev)
@@ -410,8 +412,6 @@ static int git_log_config(const char *var, const char *value, void *cb)
                if (decoration_style < 0)
                        decoration_style = 0; /* maybe warn? */
                return 0;
-       } else {
-               decoration_style = auto_decoration_style();
        }
        if (!strcmp(var, "log.showroot")) {
                default_show_root = git_config_bool(var, value);
@@ -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);
index a6c70dbe9ec84921108a85485a12111c854833b8..b376afc3124c3240f4f249ccc206efa0b064675e 100644 (file)
@@ -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
index d7ebeb4ce6b1f1a0491f8db8a66001968a41b641..ee7b293b11eb5c73dd6fe1ea31c5d3bf52c79656 100644 (file)
@@ -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);
index 30681681c1389826513464385405dfe6ef8a2855..664400b8169b673f735e77f7dd86474f1665a3ae 100644 (file)
@@ -232,6 +232,16 @@ static int split_mbox(const char *file, const char *dir, int allow_bare,
 
        do {
                peek = fgetc(f);
+               if (peek == EOF) {
+                       if (f == stdin)
+                               /* empty stdin is OK */
+                               ret = skip;
+                       else {
+                               fclose(f);
+                               error(_("empty mbox: '%s'"), file);
+                       }
+                       goto out;
+               }
        } while (isspace(peek));
        ungetc(peek, f);
 
index cfe2a796f85ecf8dfbbb35d3232893929670b492..0c36a70ad8f4dba1744ba6c4fa93389e2b796925 100644 (file)
@@ -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);
index 5b7ab9b9672e08d7db843d0ccd951ee4c7bdac15..bad6735c76fd0647547edfd1201d91c3c86dfc8f 100644 (file)
@@ -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;
 }
index 703827f00668ca0df807f35e300f0927d00075cd..a4a098f40f8e3e61c3f8b730a6b22a83c3c23566 100644 (file)
@@ -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;
index de9b40fc63b0a4f76401a111aba806c64ce36ee9..da0fd8cd706659a8784da8112cd1b3acd306375f 100644 (file)
@@ -72,7 +72,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
        unsigned mode;
        enum object_type mode_type; /* object type derived from mode */
        enum object_type obj_type; /* object type derived from sha */
-       char *path;
+       char *path, *to_free = NULL;
        unsigned char sha1[20];
 
        ptr = buf;
@@ -102,7 +102,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
                struct strbuf p_uq = STRBUF_INIT;
                if (unquote_c_style(&p_uq, path, NULL))
                        die("invalid quoting");
-               path = strbuf_detach(&p_uq, NULL);
+               path = to_free = strbuf_detach(&p_uq, NULL);
        }
 
        /*
@@ -136,6 +136,7 @@ static void mktree_line(char *buf, size_t len, int nul_term_line, int allow_miss
        }
 
        append_to_tree(mode, sha1, path);
+       free(to_free);
 }
 
 int cmd_mktree(int ac, const char **av, const char *prefix)
index 92a5d8a5d263f78afaa2f426135998348dae53b8..7fc7e66e8500b82cbfecc21a1dce77e09de28ab0 100644 (file)
 
 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;
        struct commit_list *parents;
        int parent_number = 1;
+       char *to_free = NULL;
 
        parse_commit(commit);
 
@@ -35,7 +75,7 @@ static void name_rev(struct commit *commit,
                return;
 
        if (deref) {
-               tip_name = xstrfmt("%s^0", tip_name);
+               tip_name = to_free = xstrfmt("%s^0", tip_name);
 
                if (generation)
                        die("generation: %d, but deref?", generation);
@@ -45,16 +85,18 @@ 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;
-       } else
+               name->from_tag = from_tag;
+       } else {
+               free(to_free);
                return;
+       }
 
        for (parents = commit->parents;
                        parents;
@@ -72,10 +114,12 @@ static void name_rev(struct commit *commit,
                                                   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);
                }
        }
 }
@@ -114,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;
@@ -122,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;
@@ -137,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;
@@ -194,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;
 }
@@ -216,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)
@@ -301,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;
@@ -311,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);
                        }
@@ -323,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;
@@ -374,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)
index 7b891471c461ddc3cd2b9d3186cc8b005f68ca2b..7196bff0eb2454eee86f87241706523c0266e66e 100644 (file)
@@ -340,8 +340,10 @@ static struct notes_tree *init_notes_check(const char *subcommand,
 
        ref = (flags & NOTES_INIT_WRITABLE) ? t->update_ref : t->ref;
        if (!starts_with(ref, "refs/notes/"))
-               /* TRANSLATORS: the first %s will be replaced by a
-                  git notes command: 'add', 'merge', 'remove', etc.*/
+               /*
+                * TRANSLATORS: the first %s will be replaced by a git
+                * notes command: 'add', 'merge', 'remove', etc.
+                */
                die(_("refusing to %s notes in %s (outside of refs/notes/)"),
                    subcommand, ref);
        return t;
@@ -706,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."));
index 0fe35d1b5aebd74116d66d5414bdabf19baf7d97..f672225def033595602bc4ff91ee26edd9804944 100644 (file)
@@ -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;
@@ -2717,7 +2730,11 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
  */
 static int pack_options_allow_reuse(void)
 {
-       return pack_to_stdout && allow_ofs_delta;
+       return pack_to_stdout &&
+              allow_ofs_delta &&
+              !ignore_packed_keep &&
+              (!local || !have_non_local_packs) &&
+              !incremental;
 }
 
 static int get_object_list_from_bitmap(struct rev_info *revs)
@@ -2777,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;
                        }
index 72c815844dd2abe7f2b4bc5a641eb692d2a45103..cb1df1c7614b4a91f2ed39ffe1f31dfdbcbe793e 100644 (file)
@@ -442,6 +442,7 @@ static void minimize(struct pack_list **min)
        /* return if there are no objects missing from the unique set */
        if (missing->size == 0) {
                *min = unique;
+               free(missing);
                return;
        }
 
index 42633e0c6e672c46852f0590f1941249691f962c..f0e2bff04c797b630616bddaefa742e736a423a9 100644 (file)
@@ -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
index dd1a4a94e41ed31617d31c80e097cbc044b3e3f3..da8b60fc85e93da880fb955992b8b753715fafda 100644 (file)
@@ -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 {
index a597759d8fac205f41c406d0a0e32f9240941190..258648d5fd685f4284b3a2bbc0def0076a670b28 100644 (file)
@@ -498,6 +498,10 @@ static int git_push_config(const char *k, const char *v, void *cb)
                const char *value;
                if (!git_config_get_value("push.recursesubmodules", &value))
                        recurse_submodules = parse_push_recurse_submodules_arg(k, value);
+       } else if (!strcmp(k, "submodule.recurse")) {
+               int val = git_config_bool(k, v) ?
+                       RECURSE_SUBMODULES_ON_DEMAND : RECURSE_SUBMODULES_OFF;
+               recurse_submodules = val;
        }
 
        return git_default_config(k, v, NULL);
index 23e212ee8c5b2d26f03ac6be2b822a87ad06c233..5bfd4c9f76d84c177b72a8ed97d3418d111583e9 100644 (file)
 static int nr_trees;
 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;
@@ -99,21 +98,12 @@ static int debug_merge(const struct cache_entry * const *stages,
        return 0;
 }
 
-static int option_parse_recurse_submodules(const struct option *opt,
-                                          const char *arg, int unset)
+static int git_read_tree_config(const char *var, const char *value, void *cb)
 {
-       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;
+       if (!strcmp(var, "submodule.recurse"))
+               return git_default_submodule_config(var, value, cb);
 
-       return 0;
+       return git_default_config(var, value, cb);
 }
 
 static struct lock_file lock_file;
@@ -121,7 +111,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;
@@ -157,9 +147,9 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                         N_("skip applying sparse checkout filter")),
                OPT_BOOL(0, "debug-unpack", &opts.debug_unpack,
                         N_("debug unpack-trees")),
-               { OPTION_CALLBACK, 0, "recurse-submodules", &recurse_submodules,
+               { OPTION_CALLBACK, 0, "recurse-submodules", NULL,
                            "checkout", "control recursive updating of submodules",
-                           PARSE_OPT_OPTARG, option_parse_recurse_submodules },
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
                OPT_END()
        };
 
@@ -168,18 +158,14 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
 
-       git_config(git_default_config, NULL);
+       git_config(git_read_tree_config, NULL);
 
        argc = parse_options(argc, argv, unused_prefix, read_tree_options,
                             read_tree_usage, 0);
 
-       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
+       load_submodule_cache();
 
-       if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) {
-               gitmodules_config();
-               git_config(submodule_config, NULL);
-               set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON);
-       }
+       hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
        prefix_set = opts.prefix ? 1 : 0;
        if (1 < opts.merge + opts.reset + prefix_set)
@@ -204,13 +190,13 @@ 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++;
        }
-       if (nr_trees == 0 && !read_empty)
+       if (!nr_trees && !read_empty && !opts.merge)
                warning("read-tree: emptying the index with no arguments is deprecated; use --empty");
        else if (nr_trees > 0 && read_empty)
                die("passing trees as arguments contradicts --empty");
@@ -226,9 +212,10 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
                setup_work_tree();
 
        if (opts.merge) {
-               if (stage < 2)
-                       die("just how do you expect me to merge %d trees?", stage-1);
                switch (stage - 1) {
+               case 0:
+                       die("you must specify at least one tree to merge");
+                       break;
                case 1:
                        opts.fn = opts.prefix ? bind_merge : oneway_merge;
                        break;
index f96834f42c9849746b64c20c84869232cbac7367..b1706a5731c0e527a8abc1d42ac4f0e6d346c47c 100644 (file)
@@ -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);
 }
 
@@ -473,7 +473,8 @@ static char *prepare_push_cert_nonce(const char *path, unsigned long stamp)
  * after dropping "_commit" from its name and possibly moving it out
  * of commit.c
  */
-static char *find_header(const char *msg, size_t len, const char *key)
+static char *find_header(const char *msg, size_t len, const char *key,
+                        const char **next_line)
 {
        int key_len = strlen(key);
        const char *line = msg;
@@ -486,6 +487,8 @@ static char *find_header(const char *msg, size_t len, const char *key)
                if (line + key_len < eol &&
                    !memcmp(line, key, key_len) && line[key_len] == ' ') {
                        int offset = key_len + 1;
+                       if (next_line)
+                               *next_line = *eol ? eol + 1 : eol;
                        return xmemdupz(line + offset, (eol - line) - offset);
                }
                line = *eol ? eol + 1 : NULL;
@@ -495,8 +498,8 @@ 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");
-       unsigned long stamp, ostamp;
+       char *nonce = find_header(buf, len, "nonce", NULL);
+       timestamp_t stamp, ostamp;
        char *bohmac, *expect = NULL;
        const char *retval = NONCE_BAD;
 
@@ -534,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;
@@ -552,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 &&
@@ -575,6 +578,45 @@ static const char *check_nonce(const char *buf, size_t len)
        return retval;
 }
 
+/*
+ * Return 1 if there is no push_cert or if the push options in push_cert are
+ * the same as those in the argument; 0 otherwise.
+ */
+static int check_cert_push_options(const struct string_list *push_options)
+{
+       const char *buf = push_cert.buf;
+       int len = push_cert.len;
+
+       char *option;
+       const char *next_line;
+       int options_seen = 0;
+
+       int retval = 1;
+
+       if (!len)
+               return 1;
+
+       while ((option = find_header(buf, len, "push-option", &next_line))) {
+               len -= (next_line - buf);
+               buf = next_line;
+               options_seen++;
+               if (options_seen > push_options->nr
+                   || strcmp(option,
+                             push_options->items[options_seen - 1].string)) {
+                       retval = 0;
+                       goto leave;
+               }
+               free(option);
+       }
+
+       if (options_seen != push_options->nr)
+               retval = 0;
+
+leave:
+       free(option);
+       return retval;
+}
+
 static void prepare_push_cert_sha1(struct child_process *proc)
 {
        static int already_done;
@@ -858,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);
@@ -986,7 +1028,8 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 {
        const char *name = cmd->ref_name;
        struct strbuf namespaced_name_buf = STRBUF_INIT;
-       const char *namespaced_name, *ret;
+       static char *namespaced_name;
+       const char *ret;
        struct object_id *old_oid = &cmd->old_oid;
        struct object_id *new_oid = &cmd->new_oid;
 
@@ -997,6 +1040,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
        }
 
        strbuf_addf(&namespaced_name_buf, "%s%s", get_git_namespace(), name);
+       free(namespaced_name);
        namespaced_name = strbuf_detach(&namespaced_name_buf, NULL);
 
        if (is_ref_checked_out(namespaced_name)) {
@@ -1058,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 ||
@@ -1082,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.");
@@ -1929,6 +1973,11 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 
                if (use_push_options)
                        read_push_options(&push_options);
+               if (!check_cert_push_options(&push_options)) {
+                       struct command *cmd;
+                       for (cmd = commands; cmd; cmd = cmd->next)
+                               cmd->error_string = "inconsistent push options";
+               }
 
                prepare_shallow_info(&si, &shallow);
                if (!si.nr_ours && !si.nr_theirs)
index 747277577857af2c71ab74b83d9ab3aea863c597..920c16dac025b0f5650b0f91511c22359d5bda2d 100644 (file)
@@ -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;
index addf97ad29343b2328adce2c27ccea92aee6af7a..f1a88fe2658986af2e33d3979af1764f6decc43b 100644 (file)
@@ -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) {
@@ -1151,8 +1151,11 @@ static int show(int argc, const char **argv)
                        url_nr = states.remote->url_nr;
                }
                for (i = 0; i < url_nr; i++)
-                       /* TRANSLATORS: the colon ':' should align with
-                          the one in "  Fetch URL: %s" translation */
+                       /*
+                        * TRANSLATORS: the colon ':' should align
+                        * with the one in " Fetch URL: %s"
+                        * translation.
+                        */
                        printf_ln(_("  Push  URL: %s"), url[i]);
                if (!i)
                        printf_ln(_("  Push  URL: %s"), _("(no URL)"));
@@ -1301,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;
index 677bc7c81a2be11b287f3b05f91742216fbe1d51..38ba4ef825ebf4791afb50e72e69bdb61db2aa14 100644 (file)
@@ -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)
index ab17668f4330a5e688d423a60c138768b9955e1b..c921bc976f2299adc39277a21a74abbcc0c100f7 100644 (file)
@@ -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);
index fc3b906c47bbcbefdc35b178016c031e46c06691..45001e5200cb4c5aaf0ad316cf1622f9495d851d 100644 (file)
@@ -21,6 +21,8 @@
 #include "parse-options.h"
 #include "unpack-trees.h"
 #include "cache-tree.h"
+#include "submodule.h"
+#include "submodule-config.h"
 
 static const char * const git_reset_usage[] = {
        N_("git reset [--mixed | --soft | --hard | --merge | --keep] [-q] [<commit>]"),
@@ -84,7 +86,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 +156,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 +238,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);
 }
@@ -264,6 +265,14 @@ static int reset_refs(const char *rev, const struct object_id *oid)
        return update_ref_status;
 }
 
+static int git_reset_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "submodule.recurse"))
+               return git_default_submodule_config(var, value, cb);
+
+       return git_default_config(var, value, cb);
+}
+
 int cmd_reset(int argc, const char **argv, const char *prefix)
 {
        int reset_type = NONE, update_ref_status = 0, quiet = 0;
@@ -283,18 +292,23 @@ 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", NULL,
+                           "reset", "control recursive updating of submodules",
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules_worktree_updater },
                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")),
                OPT_END()
        };
 
-       git_config(git_default_config, NULL);
+       git_config(git_reset_config, NULL);
 
        argc = parse_options(argc, argv, prefix, options, git_reset_usage,
                                                PARSE_OPT_KEEP_DASHDASH);
        parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
+       load_submodule_cache();
+
        unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash);
        if (unborn) {
                /* reset on unborn branch: treat as reset to empty tree */
@@ -303,7 +317,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 +325,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 +394,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();
index bcf77f0b8a201278f0887933e568937473eb7251..718c6059c9f570d94b0bdba46d33ed1b065a34f7 100644 (file)
@@ -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)
index 051333091062ef8e2e717ec4aef8a9bdf3b4450f..efdc14473be53ff79b13012d650ec5fded052fd9 100644 (file)
@@ -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;
index fb79dcab181558e79e3934363cac3c712ffbd3aa..b39f10fcb64f047090967dbf8b31120d2beb1b67 100644 (file)
@@ -129,7 +129,7 @@ static int check_local_mod(struct object_id *head, int index_only)
                ce = active_cache[pos];
 
                if (lstat(ce->name, &st) < 0) {
-                       if (errno != ENOENT && errno != ENOTDIR)
+                       if (!is_missing_file_error(errno))
                                warning_errno(_("failed to stat '%s'"), ce->name);
                        /* It already vanished from the working tree */
                        continue;
@@ -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);
 
index 19756595d57f27c35c72449d03a95cefd55aef15..4a6cc6f490f4e7b8a98adb362213535b6a9ae97d 100644 (file)
@@ -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));
index 566a5b6a6f8937742e83577918a94f0dcb20c66c..8cc648d85b586752d39079e75cf81a4ee685b622 100644 (file)
@@ -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)
index bdf1e88e93a61b6cbe840f4c8dd4563c1e13e337..1f74a56db749a5e1ffa06715e347cdc1b041033b 100644 (file)
@@ -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);
index 4532aa083154ae7e9be07014d3761821fa6c4ddc..8bc9997767adbb77cc8af81b2b289e22f2b61c5a 100644 (file)
@@ -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) {
index ebfc09faa0d604218af8f5815af5e5fee5915158..f99b1e5790b9b6aeafaa88f438a8a50f1538c2fe 100644 (file)
@@ -257,7 +257,7 @@ static int remove_one_path(const char *path)
  */
 static int process_lstat_error(const char *path, int err)
 {
-       if (err == ENOENT || err == ENOTDIR)
+       if (is_missing_file_error(err))
                return remove_one_path(path);
        return error("lstat(\"%s\"): %s", path, strerror(err));
 }
index 38bedf8f9fe6a718af704d8be16b7787881f8c11..05b734e6d1bd469b63aa7b51921622a590730b6d 100644 (file)
@@ -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;
index 1722a9bdc2a07ce3d98921299c6b57d03a50e870..793306ea5162f1c3e9663c0c99d693b88baaea3d 100644 (file)
@@ -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);
@@ -414,9 +414,11 @@ static void show_worktree(struct worktree *wt, int path_maxlen, int abbrev_len)
                                find_unique_abbrev(wt->head_sha1, DEFAULT_ABBREV));
                if (wt->is_detached)
                        strbuf_addstr(&sb, "(detached HEAD)");
-               else if (wt->head_ref)
-                       strbuf_addf(&sb, "[%s]", shorten_unambiguous_ref(wt->head_ref, 0));
-               else
+               else if (wt->head_ref) {
+                       char *ref = shorten_unambiguous_ref(wt->head_ref, 0);
+                       strbuf_addf(&sb, "[%s]", ref);
+                       free(ref);
+               } else
                        strbuf_addstr(&sb, "(error)");
        }
        printf("%s\n", sb.buf);
index ddb6070c4c24653a4fa8699251d11626880c2e5c..5be7ce5c730f128903ed226c45834dcb2c5c3214 100644 (file)
@@ -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);
index bbf4efa0a0a38ac8f13ee1c2178eff1d2127c802..d15db03c84556af8a47783f4d4ee76379d721020 100644 (file)
--- a/bundle.c
+++ b/bundle.c
 
 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);
                        }
index 1584e4d821440d525f10515d6a07c6506b732fec..e9a4cb6a74335cfd8972b19340b38e2210061a6d 100644 (file)
--- 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;
 };
index 345ea359638fbdfec874c55d387f9da2399338d2..ec23d8c03d10bd3815f59c2d70ae5eb45170aaad 100644 (file)
@@ -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;
 }
index 41c574663a14840aa726c46c19b071fa6555f0ef..f7b9cab7ee87dd04cecbd25f34101a58fc002014 100644 (file)
@@ -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 e1f0e182ad011d5a3b6912f48b597dcc6d1070b0..4d92aae0e818015c1ec7a6d6bc99cb1391e0d824 100644 (file)
--- 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);
 
 
index d8f0d92f2802bc500e0444d65bf0d57da3adffaf..2d98f6b2f94b12aafbebcd440b3809bf48c2aca6 100755 (executable)
@@ -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
 
index 579d540d32995715114f3b81ab5839800d9e006b..6214e6acb4a1acbc3eef764a8c610a5f6e100ee0 100755 (executable)
@@ -1,4 +1,4 @@
-#!/bin/sh
+#!/usr/bin/env bash
 #
 # Perform sanity checks on documentation and build it.
 #
@@ -7,8 +7,19 @@ set -e
 
 make check-builtins
 make check-docs
-make doc
 
+# Build docs with AsciiDoc
+make --jobs=2 doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
+! test -s stderr.log
 test -s Documentation/git.html
 test -s Documentation/git.xml
 test -s Documentation/git.1
+grep '<meta name="generator" content="AsciiDoc ' Documentation/git.html
+
+# Build docs with AsciiDoctor
+make clean
+make --jobs=2 USE_ASCIIDOCTOR=1 doc > >(tee stdout.log) 2> >(tee stderr.log >&2)
+sed '/^GIT_VERSION = / d' stderr.log
+! test -s stderr.log
+test -s Documentation/git.html
+grep '<meta name="generator" content="Asciidoctor ' Documentation/git.html
index 73c78c2b80c1a21e83f1942347a76fb76511a0d3..713f09feb04116fc1529cbca1ca9cbd38e4e4379 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -11,6 +11,7 @@
 #include "commit-slab.h"
 #include "prio-queue.h"
 #include "sha1-lookup.h"
+#include "wt-status.h"
 
 static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **);
 
@@ -18,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);
 }
 
@@ -60,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;
 
@@ -89,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;
@@ -216,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)
@@ -331,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;
 
@@ -350,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;
        }
@@ -358,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;
@@ -473,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)
@@ -562,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);
        }
@@ -598,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)
@@ -607,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;
@@ -621,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)
@@ -1589,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);
@@ -1648,10 +1649,9 @@ const char *find_commit_header(const char *msg, const char *key, size_t *out_len
 /*
  * Inspect the given string and determine the true "end" of the log message, in
  * order to find where to put a new Signed-off-by: line.  Ignored are
- * trailing comment lines and blank lines, and also the traditional
- * "Conflicts:" block that is not commented out, so that we can use
- * "git commit -s --amend" on an existing commit that forgot to remove
- * it.
+ * trailing comment lines and blank lines.  To support "git commit -s
+ * --amend" on an existing commit, we also ignore "Conflicts:".  To
+ * support "git commit -v", we truncate at cut lines.
  *
  * Returns the number of bytes from the tail to ignore, to be fed as
  * the second parameter to append_signoff().
@@ -1661,8 +1661,9 @@ int ignore_non_trailer(const char *buf, size_t len)
        int boc = 0;
        int bol = 0;
        int in_old_conflicts_block = 0;
+       size_t cutoff = wt_status_locate_end(buf, len);
 
-       while (bol < len) {
+       while (bol < cutoff) {
                const char *next_line = memchr(buf + bol, '\n', len - bol);
 
                if (!next_line)
@@ -1688,5 +1689,5 @@ int ignore_non_trailer(const char *buf, size_t len)
                }
                bol = next_line - buf;
        }
-       return boc ? len - boc : 0;
+       return boc ? len - boc : len - cutoff;
 }
index 7b1986d5c8a0120ea90ee485ba46aff912de60fa..4127c298cb1370a9ab56ca9d003e853b3e26b67e 100644 (file)
--- 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,
index 3fbfda5978b7bb715ab132283a69fa49384896e9..c6134f7c810072106026577d8ee306df9311ef24 100644 (file)
@@ -940,65 +940,15 @@ static const char *parse_interpreter(const char *cmd)
        return p+1;
 }
 
-/*
- * 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)
-               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);
@@ -1013,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;
 }
@@ -1190,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;
@@ -1202,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;
@@ -1220,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;
@@ -1253,7 +1211,6 @@ static int try_shell_exec(const char *cmd, char *const *argv)
                free(prog);
                free(argv2);
        }
-       free_path_split(path);
        return pid;
 }
 
@@ -1275,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);
@@ -1284,7 +1240,6 @@ int mingw_execvp(const char *cmd, char *const *argv)
        } else
                errno = ENOENT;
 
-       free_path_split(path);
        return -1;
 }
 
index 33501695550accdb08368aa867b9addb25c90d5f..e03aecfe2e6556e1ef513922104557373eaa9260 100644 (file)
@@ -398,7 +398,11 @@ HANDLE winansi_get_osfhandle(int fd);
        (isalpha(*(path)) && (path)[1] == ':' ? 2 : 0)
 int mingw_skip_dos_drive_prefix(char **path);
 #define skip_dos_drive_prefix mingw_skip_dos_drive_prefix
-#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
+static inline int mingw_is_dir_sep(int c)
+{
+       return c == '/' || c == '\\';
+}
+#define is_dir_sep mingw_is_dir_sep
 static inline char *mingw_find_last_dir_sep(const char *path)
 {
        char *ret = NULL;
index 793420f9d0d7732cb7dc2aa94845a22086fe7021..a11a0f16d276470381587236ae513994d92af477 100644 (file)
@@ -105,6 +105,13 @@ static int is_console(int fd)
        if (!fd) {
                if (!GetConsoleMode(hcon, &mode))
                        return 0;
+               /*
+                * This code path is only reached if there is no console
+                * attached to stdout/stderr, i.e. we will not need to output
+                * any text to any console, therefore we might just as well
+                * use black as foreground color.
+                */
+               sbi.wAttributes = 0;
        } else if (!GetConsoleScreenBufferInfo(hcon, &sbi))
                return 0;
 
@@ -133,6 +140,11 @@ static void write_console(unsigned char *str, size_t len)
 
        /* convert utf-8 to utf-16 */
        int wlen = xutftowcsn(wbuf, (char*) str, ARRAY_SIZE(wbuf), len);
+       if (wlen < 0) {
+               wchar_t *err = L"[invalid]";
+               WriteConsoleW(console, err, wcslen(err), &dummy, NULL);
+               return;
+       }
 
        /* write directly to console */
        WriteConsoleW(console, wbuf, wlen, &dummy, NULL);
index b4a3205da32faf43db1ab990f08c0bb941af87d0..146cb3452adab3115f15d30c2b0f9f5480344279 100644 (file)
--- 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);
        }
@@ -2621,7 +2637,7 @@ int git_config_rename_section_in_file(const char *config_filename,
        struct lock_file *lock;
        int out_fd;
        char buf[1024];
-       FILE *config_file;
+       FILE *config_file = NULL;
        struct stat st;
 
        if (new_name && !section_name_is_ok(new_name)) {
@@ -2703,11 +2719,14 @@ int git_config_rename_section_in_file(const char *config_filename,
                }
        }
        fclose(config_file);
+       config_file = NULL;
 commit_and_out:
        if (commit_lock_file(lock) < 0)
                ret = error_errno("could not write config file %s",
                                  config_filename);
 out:
+       if (config_file)
+               fclose(config_file);
        rollback_lock_file(lock);
 out_no_rollback:
        free(filename_buf);
index 399fe192719072691ebdbd7954fd8bd6cc12b96e..192629f1431072f242f10a8528f1853b8bbaddeb 100644 (file)
@@ -237,6 +237,7 @@ ifeq ($(uname_S),AIX)
        NO_MKDTEMP = YesPlease
        NO_STRLCPY = YesPlease
        NO_NSEC = YesPlease
+       NO_REGEX = NeedsStartEnd
        FREAD_READS_DIRECTORIES = UnfortunatelyYes
        INTERNAL_QSORT = UnfortunatelyYes
        NEEDS_LIBICONV = YesPlease
index 128165529fd70ac889ed0fb5098e4290893b593d..deeb968daa07355b729d178644c843c7a0ed62c5 100644 (file)
@@ -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
index cd21a1b6f725fc80e40759a8f9b26450633df6a8..c72b1d1151744c5e7c7b82f453892a1bc9b26021 100644 (file)
--- 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/.gitattributes b/contrib/completion/.gitattributes
new file mode 100644 (file)
index 0000000..1911694
--- /dev/null
@@ -0,0 +1 @@
+*.bash eol=lf
index d18d30b7775c89efea7f43b88aeab0073bfed2f5..48a2f26622d992b89462edb3658b5cdccee806bc 100644 (file)
@@ -1319,6 +1319,7 @@ _git_clone ()
                        --template=
                        --depth
                        --single-branch
+                       --no-tags
                        --branch
                        --recurse-submodules
                        --no-single-branch
@@ -2396,7 +2397,9 @@ _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
@@ -2840,7 +2843,7 @@ _git_show_branch ()
 _git_stash ()
 {
        local save_opts='--all --keep-index --no-keep-index --quiet --patch --include-untracked'
-       local subcommands='save list show apply clear drop pop create branch'
+       local subcommands='push save list show apply clear drop pop create branch'
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
                case "$cur" in
@@ -2855,6 +2858,9 @@ _git_stash ()
                esac
        else
                case "$subcommand,$cur" in
+               push,--*)
+                       __gitcomp "$save_opts --message"
+                       ;;
                save,--*)
                        __gitcomp "$save_opts"
                        ;;
index f784dd2e66b9373285027dc1c47948231b172f46..7c4cd8d257da0d64098f6b31834280e90f6e4f31 100644 (file)
@@ -35,6 +35,16 @@ to use persistent-https:
 [url "persistent-http"]
        insteadof = http
 
+You may also want to allow the use of the persistent-https helper for
+submodule URLs (since any https URLs pointing to submodules will be
+rewritten, and Git's out-of-the-box defaults forbid submodules from
+using unknown remote helpers):
+
+[protocol "persistent-https"]
+       allow = always
+[protocol "persistent-http"]
+       allow = always
+
 
 #####################################################################
 # BUILDING FROM SOURCE
diff --git a/contrib/workdir/.gitattributes b/contrib/workdir/.gitattributes
new file mode 100644 (file)
index 0000000..1f78c5d
--- /dev/null
@@ -0,0 +1 @@
+/git-new-workdir eol=lf
index 8d652bf27c9444d3696c4b942dc85f062e2bf53e..f1e168bc303c71542b692b2b9d08cdb2e2c98669 100644 (file)
--- 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 @@ static int apply_multi_file_filter(const char *path, const char *src, size_t len
                         * 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);
index 46c5937526a53c3563ea47bfba8195bcf9352c35..f3814cc47a059227e24ca52007c7e19fdf2db480 100644 (file)
@@ -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 a996331f5b33703c9f70844c6b49453ec39d16a7..63fa99685e288bd79c75fd6af983f8b628a08fed 100644 (file)
--- 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;
 
        /*
index 52447466b5d16c4e8978795b0122e2182e83c5cb..76c8f185cd032b92e010b95e4d92e3b08a6ab18a 100644 (file)
@@ -29,7 +29,7 @@
 static int check_removed(const struct cache_entry *ce, struct stat *st)
 {
        if (lstat(ce->name, st) < 0) {
-               if (errno != ENOENT && errno != ENOTDIR)
+               if (!is_missing_file_error(errno))
                        return -1;
                return 1;
        }
@@ -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 74283d9001fbc48d11ba5c449ea7c16692791c88..5275c4b78011e90627c073f487f3f8298b483352 100644 (file)
--- 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 5be1ee77a759f330db4ace64a5772f23e3d19d50..67537f17ed4ae1e64a60a4bdaaa97625cb3ffd15 100644 (file)
--- 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 f451bfa48c0a0e7905d4c2adf4e3e05a8d272a8a..70f2de38f23ce5f9e4c107cfdb0f7901dfd22b84 100644 (file)
--- 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;
                }
@@ -2272,7 +2337,7 @@ int remove_path(const char *name)
 {
        char *slash;
 
-       if (unlink(name) && errno != ENOENT && errno != ENOTDIR)
+       if (unlink(name) && !is_missing_file_error(errno))
                return -1;
 
        slash = strrchr(name, '/');
diff --git a/dir.h b/dir.h
index bf23a470af57775c8f0099597f7a403995bf3a89..a89c13e27a4fb55883f811a2c82fb465bb14988d 100644 (file)
--- 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 d2b512da90a3cc84d592a2024cc8dac363c9418d..d6b263f78e0d2dfef997d94905412b0543a8b8c7 100644 (file)
--- 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)
index ff6e4f06e93d642aa53ba1812c3788b0ccad092e..aa478e71de9d9c233593de050d6823e96be77b88 100644 (file)
@@ -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;
 }
 
index cf58f875b889eb0360df1f58cacf4f4017c6c10f..e69d219682e7929b6e8441ba620e708704471ff3 100644 (file)
@@ -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 @@ static int tree_content_remove(
                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)
index afb8b05024823981be9bedfaa70f7301c6f7076c..cd86865beba62ffade9f8aa63427781f301e79e7 100644 (file)
@@ -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 @@ static int find_common(struct fetch_pack_args *args,
        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 @@ static int find_common(struct fetch_pack_args *args,
 
 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 e6152e4e6d426bd92ae7ae063346f25f85ddd8de..b4204d772b39335c5feb5c85a11c517bcedcd562 100644 (file)
--- 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");
index 709a5f6ce6fbdb2da14084e94ae9df1db1c3d0a6..79d675b5b02ee73a025f86acc6cc29e530ee8db6 100755 (executable)
@@ -46,7 +46,6 @@
 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);
        }
index bd04564a69a052ce856f2ba120855541c66fc650..e83fd2eb07729561d03566cdc312636525a83ee6 100644 (file)
@@ -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
@@ -1121,6 +1134,21 @@ struct tm *git_gmtime_r(const time_t *, struct tm *);
 #define getc_unlocked(fh) getc(fh)
 #endif
 
+/*
+ * Our code often opens a path to an optional file, to work on its
+ * contents when we can successfully open it.  We can ignore a failure
+ * to open if such an optional file does not exist, but we do want to
+ * report a failure in opening for other reasons (e.g. we got an I/O
+ * error, or the file is there, but we lack the permission to open).
+ *
+ * Call this function after seeing an error from open() or fopen() to
+ * see if the errno indicates a missing file that we can safely ignore.
+ */
+static inline int is_missing_file_error(int errno_)
+{
+       return (errno_ == ENOENT || errno_ == ENOTDIR);
+}
+
 extern int cmd_main(int, const char **);
 
 #endif
index 2b8cdba157d9cd822acc88b7ec58cecda0149b85..aafaf708dace3493bee23370a6af9535a38e4c99 100755 (executable)
@@ -239,7 +239,7 @@ git rev-parse --no-flags --revs-only --symbolic-full-name \
 sed -e '/^^/d' "$tempdir"/raw-heads >"$tempdir"/heads
 
 test -s "$tempdir"/heads ||
-       die "Which ref do you want to rewrite?"
+       die "You must specify a ref to rewrite."
 
 GIT_INDEX_FILE="$(pwd)/../index"
 export GIT_INDEX_FILE
index 33d07c06bd90833ce56bc64c13bdc08c1997c3fb..59cd41dbff72e3073f5ef0380350ec9ea3c4af5b 100644 (file)
@@ -2,3 +2,4 @@
 *           encoding=US-ASCII
 git-gui.sh  encoding=UTF-8
 /po/*.po    encoding=UTF-8
+/GIT-VERSION-GEN eol=lf
index 2c9c0165b5ab0cee8c9369b9bee33277bd8bc5e9..90b1fbe9cf6e8dfb2f4331916809fa40bf9050d2 100644 (file)
@@ -5,7 +5,7 @@
 # Copyright (c) 2006 Johannes E. Schindelin
 #
 # The original idea comes from Eric W. Biederman, in
-# http://article.gmane.org/gmane.comp.version-control.git/22407
+# https://public-inbox.org/git/m1odwkyuf5.fsf_-_@ebiederm.dsl.xmission.com/
 #
 # The file containing rebase commands, comments, and empty lines.
 # This file is created by "git rebase -i" then edited by the user.  As
index eea0a517f71b66f861db38f70fac0593f8c8cea4..7fd58744360a2bb14531ed3f73092b45449d5590 100755 (executable)
@@ -25,8 +25,9 @@
 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 @@ sub send_message {
                        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 @@ sub send_message {
                        # 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>) {
index 7cf68f07b773f7e520c1e6ba680bff65e5bc7f3a..d8209c7a02d8753af4724f385a4ea783384536f6 100755 (executable)
@@ -8085,7 +8085,7 @@ sub git_search_help {
 <p><strong>Pattern</strong> is by default a normal string that is matched precisely (but without
 regard to case, except in the case of pickaxe). However, when you check the <em>re</em> checkbox,
 the pattern entered is recognized as the POSIX extended
-<a href="http://en.wikipedia.org/wiki/Regular_expression">regular expression</a> (also case
+<a href="https://en.wikipedia.org/wiki/Regular_expression">regular expression</a> (also case
 insensitive).</p>
 <dl>
 <dt><b>commit</b></dt>
diff --git a/grep.c b/grep.c
index 47cee4506703f5dd2b7db84e09dae93adf99fbaf..d03d424e5cf8d32d91e3082555d8add674e01486 100644 (file)
--- 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 267534ca24df532505854e1af31c53ed01bab24e..38ac82b6384f8c6a505a8ed764610aa8404f8597 100644 (file)
--- 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 bc6cd19cf3a5e4300e32cbdd437b2d9231fd2d89..db7f3d79a016881639a8c0640451afe35b011e5e 100644 (file)
--- 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)
@@ -411,8 +370,8 @@ const char *help_unknown_cmd(const char *cmd)
 
        if (SIMILAR_ENOUGH(best_similarity)) {
                fprintf_ln(stderr,
-                          Q_("\nDid you mean this?",
-                             "\nDid you mean one of these?",
+                          Q_("\nThe most similar command is",
+                             "\nThe most similar commands are",
                           n));
 
                for (i = 0; i < n; i++)
index eef0a361f4f9bcc7f8fb278268334cb40a32fcb3..ba5ff1aa2994633c055611ce566ea8cf3c09e8cc 100644 (file)
@@ -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;
 
index f0e3108f716f8376ba924c4db1ea6f44b8bd60b9..67c4d4b472cb1065bcfa20007fdbca55ac8e0052 100644 (file)
@@ -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,"
index a23b910471b6c3195e18aad3cbdf225ae200cca0..b9087814b8ccd0082efc41c617e33e8046ea17d5 100644 (file)
@@ -1125,6 +1125,7 @@ static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *c
        changed = process_all_files(&parent_range, rev, &queue, range);
        if (parent)
                add_line_range(rev, parent, parent_range);
+       free_line_log_data(parent_range);
        return changed;
 }
 
index f3ca6aafb799eadc6255d61711d0aba8cb516463..b3931fa434dc99a481569697585e3bb21190c932 100644 (file)
@@ -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);
        }
index 7b715f9e7754882061dde0220d13fdc7d0d6c252..572064939c718281b56abda7757bb21d35f42de3 100644 (file)
@@ -175,6 +175,14 @@ static inline int hold_lock_file_for_update(
        return hold_lock_file_for_update_timeout(lk, path, flags, 0);
 }
 
+/*
+ * 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
index 4618dd04ca1b6bd2bf53481f39851fb4e5d02336..a4ec11c2bf62bbda4304d19913f25965247d4027 100644 (file)
@@ -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};
 
index 68037758f2f01be6edfab34b7871af68a17163eb..f92cb9f729ca05ae1e80c9c27bc102dbb64a8dc0 100644 (file)
@@ -882,7 +882,10 @@ static int read_one_header_line(struct strbuf *line, FILE *in)
        for (;;) {
                int peek;
 
-               peek = fgetc(in); ungetc(peek, in);
+               peek = fgetc(in);
+               if (peek == EOF)
+                       break;
+               ungetc(peek, in);
                if (peek != ' ' && peek != '\t')
                        break;
                if (strbuf_getline_lf(&continuation, in))
@@ -1099,6 +1102,10 @@ int mailinfo(struct mailinfo *mi, const char *msg, const char *patch)
 
        do {
                peek = fgetc(mi->input);
+               if (peek == EOF) {
+                       fclose(cmitmsg);
+                       return error("empty patch: '%s'", patch);
+               }
        } while (isspace(peek));
        ungetc(peek, mi->input);
 
index 62decd51cc74a9197abbda28dc8efd6f83287a6b..ae5238d82ca8c86b99391a0836a0539efe7eacde 100644 (file)
@@ -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 04ee5fc911c8d8134decb5c1a400576ae5681bf6..1d441ad94254257ad2ac28b8a17dc0b8e5ce14f9 100644 (file)
--- 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];
index 5dfc5cbd08e496748880bda6a67264c7d63f68b4..2843e985760ae2b48f2f8103c573cee92fc1c350 100644 (file)
@@ -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);
 }
index 356f88fb3c63786348a85cc913502245dd526808..aeeee8409d4320644a1f618f5cd25b815be52f1c 100644 (file)
@@ -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 */
index 32caaaff7477d3640c81a19c54c1c92ffb8ca8b1..6244f6af9c655203953d91977f08dc982393ce78 100644 (file)
@@ -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);
index 24a33616a47737e6ae8cf917080cbb314dcf2762..031503d7b2ba9217cffa52652b57aa063208fb1a 100644 (file)
@@ -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);
@@ -132,8 +132,11 @@ struct notes_rewrite_cfg *init_copy_notes_for_rewrite(const char *cmd)
                c->mode_from_env = 1;
                c->combine = parse_combine_notes_fn(rewrite_mode_env);
                if (!c->combine)
-                       /* TRANSLATORS: The first %s is the name of the
-                          environment variable, the second %s is its value */
+                       /*
+                        * TRANSLATORS: The first %s is the name of
+                        * the environment variable, the second %s is
+                        * its value.
+                        */
                        error(_("Bad %s value: '%s'"), GIT_NOTES_REWRITE_MODE_ENVIRONMENT,
                                        rewrite_mode_env);
        }
index e680d881a45756eb234a80a9909c24a7b146c1f7..06ba3a11d87ad7b93f29aef535e773fcc4683004 100644 (file)
--- 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;
index f52957dcb34a3340de2d398014ad4605e7620360..33e5cc9943eeff8618493fbd750aecde0b836710 100644 (file)
--- 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);
index e313f4f2bc69f967ec4354e80cd62b329c7bcb21..8e47a96b3bb68f8d5ccb87f1c955863aeda39bf6 100644 (file)
@@ -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,
index 39bcc168463fc7358ab7cb4e963e6ed8bde94804..a3ac3dccd4f8423fcd39ba0ee77a200500d75a81 100644 (file)
@@ -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)
index 27f70d345fbf1b339cb4bad1011253cece1bf619..e1fcb228fa126e34528e9d5a38b81a6b2eae2f2a 100644 (file)
@@ -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;
                }
index 6398a8aa96f2f44c0b01cf168619f1d269bc6dbb..9558d13834e2842d32fa4015f2e3c00538d52dcd 100644 (file)
@@ -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);
index fa97b72559883a38be147fab737f6b7adae5975b..a333ec675476b4f10a5e12fe3303333ea200faea 100644 (file)
@@ -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 5c2158746ed60f5c1d4f89dce04312e5992b959d..8294341af174ad67693006dfcb49a7208732f45b 100644 (file)
--- 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);
index 7419780a9b644e74107eec31d1c75156374920b8..c6679cb2cdee15981ee9ef31c402c358a3727d1e 100644 (file)
@@ -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);
index a23a1e67f04f8072bea357cbed34fdc1b39ab7ae..e5ad34a2c3f7c7d7fdb65171f8a142a2becc64bb 100644 (file)
@@ -589,8 +589,10 @@ static int usage_with_options_internal(struct parse_opt_ctx_t *ctx,
 
        fprintf_ln(outfile, _("usage: %s"), _(*usagestr++));
        while (*usagestr && **usagestr)
-               /* TRANSLATORS: the colon here should align with the
-                  one in "usage: %s" translation */
+               /*
+                * TRANSLATORS: the colon here should align with the
+                * one in "usage: %s" translation.
+                */
                fprintf_ln(outfile, _("   or: %s"), _(*usagestr++));
        while (*usagestr) {
                if (**usagestr)
index fa8f11de82657c3edf4e5c0869d76ae6f12b57bb..92eba7a059e89873e2d94fd5ee37a92b887bde4c 100644 (file)
@@ -99,11 +99,12 @@ struct patch_id *has_commit_patch_id(struct commit *commit,
 struct patch_id *add_commit_patch_id(struct commit *commit,
                                     struct patch_ids *ids)
 {
-       struct patch_id *key = xcalloc(1, sizeof(*key));
+       struct patch_id *key;
 
        if (!patch_id_defined(commit))
                return NULL;
 
+       key = xcalloc(1, sizeof(*key));
        if (init_patch_id_entry(key, commit, ids)) {
                free(key);
                return NULL;
index 50f76fff458e45f57ae67c3474de8378f390f978..828405021fca4214585e1d46b65f604d14e61143 100644 (file)
@@ -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");
        }
 }
 
index 55e976972cecfa5f0c204266c5d2a3a4d3d306a0..60e6500401e687bc0d764c38c5d8b4928b7bfde8 100644 (file)
@@ -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 */
index d4b6bfe076341d64bb948e4932655b927a369bc0..9d845ecc3ccc65194852a1821432cec771ac49dc 100644 (file)
@@ -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);
index 18eac6483095319806f4c9b301b0f84e0c68d38c..450183b6496fce4ec3077322f61623152c87cf07 100644 (file)
@@ -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);
 
@@ -73,6 +75,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.
index d0f86f5d85cab6c470871cdd6e5ead526259bf6a..09701bd2ffef3eb6d9104916e4119f757c06c244 100644 (file)
--- 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 */
index a8a979bd4fcbac6732d26dda3f826a1eec3d3a6b..c62efbfd43b9c4ddc25d6768d55bddc745f5c14b 100644 (file)
@@ -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;
 
index d23efc36ec5f198b9edab76f2aadff9461b83f45..3c00fa0526cd97e36111f2d94ccbf3d2817ccf34 100644 (file)
@@ -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
index 0d0081a11b858227d3f55ebbc21f04bd49875f68..bc156a133e9ff197e3d98c63d66f2d958f47f564 100644 (file)
@@ -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;
+}
index 3a640448fd83e6cf0bda8ddda081757d5941361e..ab32bc9c3145c85c839c660c54b61f13fe9cb51a 100644 (file)
@@ -93,6 +93,7 @@ static struct used_atom {
                        unsigned int length;
                } objectname;
                struct refname_atom refname;
+               char *head;
        } u;
 } *used_atom;
 static int used_atom_cnt, need_tagged, need_symref;
@@ -287,6 +288,12 @@ static void if_atom_parser(struct used_atom *atom, const char *arg)
        }
 }
 
+static void head_atom_parser(struct used_atom *atom, const char *arg)
+{
+       struct object_id unused;
+
+       atom->u.head = resolve_refdup("HEAD", RESOLVE_REF_READING, unused.hash, NULL);
+}
 
 static struct {
        const char *name;
@@ -325,7 +332,7 @@ static struct {
        { "push", FIELD_STR, remote_ref_atom_parser },
        { "symref", FIELD_STR, refname_atom_parser },
        { "flag" },
-       { "HEAD" },
+       { "HEAD", FIELD_STR, head_atom_parser },
        { "color", FIELD_STR, color_atom_parser },
        { "align", FIELD_STR, align_atom_parser },
        { "end" },
@@ -351,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;
 };
 
@@ -677,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;
@@ -723,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)
@@ -770,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;
@@ -849,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;
@@ -868,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 */
@@ -1251,13 +1258,17 @@ char *get_head_description(void)
                            state.branch);
        else if (state.detached_from) {
                if (state.detached_at)
-                       /* TRANSLATORS: make sure this matches
-                          "HEAD detached at " in wt-status.c */
+                       /*
+                        * TRANSLATORS: make sure this matches "HEAD
+                        * detached at " in wt-status.c
+                        */
                        strbuf_addf(&desc, _("(HEAD detached at %s)"),
                                state.detached_from);
                else
-                       /* TRANSLATORS: make sure this matches
-                          "HEAD detached from " in wt-status.c */
+                       /*
+                        * TRANSLATORS: make sure this matches "HEAD
+                        * detached from " in wt-status.c
+                        */
                        strbuf_addf(&desc, _("(HEAD detached from %s)"),
                                state.detached_from);
        }
@@ -1293,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));
 
@@ -1366,15 +1377,10 @@ 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")) {
-                       const char *head;
-                       unsigned char sha1[20];
-
-                       head = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-                                                 sha1, NULL);
-                       if (head && !strcmp(ref->refname, head))
+                       if (atom->u.head && !strcmp(ref->refname, atom->u.head))
                                v->s = "*";
                        else
                                v->s = " ";
@@ -1415,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)
@@ -1438,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
@@ -1449,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);
@@ -1665,6 +1671,68 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
        return match_pattern(filter, 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
@@ -1687,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)
@@ -1704,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;
@@ -1782,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... */
@@ -1911,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);
        }
@@ -1941,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;
@@ -2090,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) {
@@ -2105,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);
 
index c20167aa3c785f0060147204d963537f58617570..6552024f09e4d4d587eaf49377e7504a79818c1d 100644 (file)
@@ -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;
index 99679f58255e386526335da8342e17356b48a4f9..ed99437ad2068a75d14e739354e1bc17932fd839 100644 (file)
@@ -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;
@@ -183,7 +183,11 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                if (!reflogs || reflogs->nr == 0) {
                        struct object_id oid;
                        char *b;
-                       if (dwim_log(branch, strlen(branch), oid.hash, &b) == 1) {
+                       int ret = dwim_log(branch, strlen(branch),
+                                          oid.hash, &b);
+                       if (ret > 1)
+                               free(b);
+                       else if (ret == 1) {
                                if (reflogs) {
                                        free(reflogs->ref);
                                        free(reflogs);
@@ -193,17 +197,27 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                                reflogs = read_complete_reflog(branch);
                        }
                }
-               if (!reflogs || reflogs->nr == 0)
+               if (!reflogs || reflogs->nr == 0) {
+                       if (reflogs) {
+                               free(reflogs->ref);
+                               free(reflogs);
+                       }
+                       free(branch);
                        return -1;
+               }
                string_list_insert(&info->complete_reflogs, branch)->util
                        = reflogs;
        }
+       free(branch);
 
        commit_reflog = xcalloc(1, sizeof(struct commit_reflog));
        if (recno < 0) {
                commit_reflog->recno = get_reflog_recno_by_time(reflogs, timestamp);
                if (commit_reflog->recno < 0) {
-                       free(branch);
+                       if (reflogs) {
+                               free(reflogs->ref);
+                               free(reflogs);
+                       }
                        free(commit_reflog);
                        return -1;
                }
@@ -238,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 df75f8e0d695fc235c1a2497069a9b916a4a04e8..f0685c92513e306188d89380a1c3020024a4edb4 100644 (file)
--- 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 07cf4cd41b15ee5dafdb5c99983b136db84aaa81..4be14c4b3cc65de453c4eae438c245b0c91355e5 100644 (file)
--- 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,19 +565,47 @@ 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
@@ -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 */
index 83ea080e013f90e95d1897258dd80b44ac9b5db6..d8b3f73147c8af88597ad71259aef0a9f53dd104 100644 (file)
@@ -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 @@ static int files_transaction_commit(struct ref_store *ref_store,
                }
        }
 
+       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,
index bce1f192f7d4c7e2e3efc6d5ea7df4560cb3cc0a..4cf449ef660e2bc9ee5b6ddb4efd8662d27e12b3 100644 (file)
@@ -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;
index 6059362f1dd783977f21c36302b7aec3b18a7f38..af2fcb2c1217bc89e095515f6dcfcfb1c0e2befa 100644 (file)
@@ -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
@@ -373,6 +423,13 @@ struct cache_ref_iterator {
        /* The number of levels that have been allocated on the stack */
        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
@@ -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;
 }
index ffdc54f3f07961b1ddd6b56c64973e40c18583bd..794f000fd31ebad3ce340ffd9af3fc843b089479 100644 (file)
@@ -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);
index 3d46131efbcc0a76cc3054a97a669bc5bdd036e7..192f9f85c97c0d7ca4e9c933e27ba0686da31dbe 100644 (file)
@@ -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,
@@ -169,6 +169,14 @@ int refs_read_raw_ref(struct ref_store *ref_store,
                      const char *refname, unsigned char *sha1,
                      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
@@ -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;
index 801137c72ebb2edf196811a8031db603c61f376d..3649d60cdce7335f6bc5bcca4810ce869dacfdcf 100644 (file)
--- 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);
@@ -649,7 +633,12 @@ static int valid_remote_nick(const char *name)
 {
        if (!name[0] || is_dot_or_dotdot(name))
                return 0;
-       return !strchr(name, '/'); /* no slash */
+
+       /* remote nicknames cannot contain slashes */
+       while (*name)
+               if (is_dir_sep(*name++))
+                       return 0;
+       return 1;
 }
 
 const char *remote_for_branch(struct branch *branch, int *explicit)
@@ -1191,9 +1180,10 @@ static int match_explicit(struct ref *src, struct ref *dst,
                else if (is_null_oid(&matched_src->new_oid))
                        error("unable to delete '%s': remote ref does not exist",
                              dst_value);
-               else if ((dst_guess = guess_ref(dst_value, matched_src)))
+               else if ((dst_guess = guess_ref(dst_value, matched_src))) {
                        matched_dst = make_linked_ref(dst_guess, dst_tail);
-               else
+                       free(dst_guess);
+               } else
                        error("unable to push to unqualified destination: %s\n"
                              "The destination refspec neither matches an "
                              "existing ref on the remote nor\n"
@@ -1296,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;
@@ -1358,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;
@@ -1585,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;
@@ -1953,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;
@@ -2009,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;
 
index 7ff61ff5f73db07af522ac57d62ed9e699aedf72..f88c14bab3188c65631593640289b26d00a6a3fc 100644 (file)
@@ -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,9 +228,9 @@ 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 (flags & UNINTERESTING)
+                       if (revs->ignore_missing_links || (flags & UNINTERESTING))
                                return NULL;
                        die("bad object %s", oid_to_hex(&tag->tagged->oid));
                }
@@ -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);
        }
 
index 14886ec92b4f67527a86fc821998214a4d2b321f..a91dd3d5d97d9e609d770f418cb9f804b28e752d 100644 (file)
@@ -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);
index 574b81d3e82bbe6de31b141c3951ab408daad5e6..9e36151bf97d36945ca3464e719687718ee6d1c7 100644 (file)
@@ -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 @@ int start_command(struct child_process *cmd)
 #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
 {
index 4fa8f65adbdcea4d007435d9c1d54c622df54474..3932420ec8a560479c9697e3626e9da15e952853 100644 (file)
@@ -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 *);
index 10c3b4ff81547891172319cee0e291c5160050e1..924fb1d0c3c84dd823393d7dea21af2d70a8d54e 100644 (file)
@@ -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;
@@ -464,7 +464,8 @@ static int do_recursive_merge(struct commit *base, struct commit *next,
 
        if (active_cache_changed &&
            write_locked_index(&the_index, &index_lock, COMMIT_LOCK))
-               /* TRANSLATORS: %s will be "revert", "cherry-pick" or
+               /*
+                * TRANSLATORS: %s will be "revert", "cherry-pick" or
                 * "rebase -i".
                 */
                return error(_("%s: Unable to write new index file"),
@@ -482,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
@@ -508,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)
@@ -834,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"));
@@ -934,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 };
@@ -948,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);
        }
@@ -989,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;
@@ -1045,6 +1047,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        strbuf_addstr(&msgbuf, p);
 
                if (opts->record_origin) {
+                       strbuf_complete_line(&msgbuf);
                        if (!has_conforming_footer(&msgbuf, NULL, 0))
                                strbuf_addch(&msgbuf, '\n');
                        strbuf_addstr(&msgbuf, cherry_picked_prefix);
@@ -1080,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,
@@ -1096,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);
        }
@@ -1221,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;
 
@@ -1270,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");
@@ -1279,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;
 }
 
@@ -1913,11 +1916,13 @@ static int apply_autostash(struct replay_opts *opts)
        strbuf_trim(&stash_sha1);
 
        child.git_cmd = 1;
+       child.no_stdout = 1;
+       child.no_stderr = 1;
        argv_array_push(&child.args, "stash");
        argv_array_push(&child.args, "apply");
        argv_array_push(&child.args, stash_sha1.buf);
        if (!run_command(&child))
-               printf(_("Applied autostash."));
+               printf(_("Applied autostash.\n"));
        else {
                struct child_process store = CHILD_PROCESS_INIT;
 
@@ -2088,6 +2093,7 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                                res = error(_("could not read orig-head"));
                                goto cleanup_head_ref;
                        }
+                       strbuf_reset(&buf);
                        if (!read_oneliner(&buf, rebase_path_onto(), 0)) {
                                res = error(_("could not read 'onto'"));
                                goto cleanup_head_ref;
@@ -2280,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);
@@ -2288,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));
                        }
@@ -2334,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;
@@ -2357,6 +2363,9 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
                                getenv("GIT_COMMITTER_EMAIL")));
        strbuf_addch(&sob, '\n');
 
+       if (!ignore_footer)
+               strbuf_complete_line(msgbuf);
+
        /*
         * If the whole message buffer is equal to the sob, pretend that we
         * found a conforming footer with a matching sob
@@ -2377,13 +2386,6 @@ void append_signoff(struct strbuf *msgbuf, int ignore_footer, unsigned flag)
                         * the title and body to be filled in by the user.
                         */
                        append_newlines = "\n\n";
-               } else if (msgbuf->buf[len - 1] != '\n') {
-                       /*
-                        * Incomplete line.  Complete the line and add a
-                        * blank one so that there is an empty line between
-                        * the message body and the sob.
-                        */
-                       append_newlines = "\n\n";
                } else if (len == 1) {
                        /*
                         * Buffer contains a single newline.  Add another
index f6c1a3dfb04bff5310a1b930b03478d88c9c2904..6f865b73a3aa014a9cecb13dbf86dede70a02a59 100644 (file)
@@ -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/setup.c b/setup.c
index 0309c278218f96cb6b11e6a9d60011efce54cf62..ba6e855178847a37044eacceae0ebcb17d8e990b 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -150,7 +150,7 @@ int check_filename(const char *prefix, const char *arg)
                free(to_free);
                return 1; /* file exists */
        }
-       if (errno == ENOENT || errno == ENOTDIR) {
+       if (is_missing_file_error(errno)) {
                free(to_free);
                return 0; /* file does not exist */
        }
@@ -703,11 +703,16 @@ static const char *setup_discovered_git_dir(const char *gitdir,
 
        /* --work-tree is set without --git-dir; use discovered one */
        if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
+               char *to_free = NULL;
+               const char *ret;
+
                if (offset != cwd->len && !is_absolute_path(gitdir))
-                       gitdir = real_pathdup(gitdir, 1);
+                       gitdir = to_free = real_pathdup(gitdir, 1);
                if (chdir(cwd->buf))
                        die_errno("Could not come back to cwd");
-               return setup_explicit_git_dir(gitdir, cwd, nongit_ok);
+               ret = setup_explicit_git_dir(gitdir, cwd, nongit_ok);
+               free(to_free);
+               return ret;
        }
 
        /* #16.2, #17.2, #20.2, #21.2, #24, #25, #28, #29 (see t1510) */
@@ -748,7 +753,7 @@ static const char *setup_bare_git_dir(struct strbuf *cwd, int offset,
 
        /* --work-tree is set without --git-dir; use discovered one */
        if (getenv(GIT_WORK_TREE_ENVIRONMENT) || git_work_tree_cfg) {
-               const char *gitdir;
+               static const char *gitdir;
 
                gitdir = offset == cwd->len ? "." : xmemdupz(cwd->buf, offset);
                if (chdir(cwd->buf))
index 8eec9f7c1bb59b8124e098367b7bff552bcafcf4..5126853bb5bda5f291039588db96064ffbd1ee5c 100644 (file)
@@ -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);
@@ -1408,7 +1408,7 @@ static void diagnose_invalid_sha1_path(const char *prefix,
        if (file_exists(filename))
                die("Path '%s' exists on disk, but not in '%.*s'.",
                    filename, object_name_len, object_name);
-       if (errno == ENOENT || errno == ENOTDIR) {
+       if (is_missing_file_error(errno)) {
                char *fullname = xstrfmt("%s%s", prefix, filename);
 
                if (!get_tree_entry(tree_sha1, fullname,
@@ -1473,7 +1473,7 @@ static void diagnose_invalid_index_path(int stage,
 
        if (file_exists(filename))
                die("Path '%s' exists on disk, but not in the index.", filename);
-       if (errno == ENOENT || errno == ENOTDIR)
+       if (is_missing_file_error(errno))
                die("Path '%s' does not exist (neither on disk nor in the index).",
                    filename);
 
@@ -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/sha1dc/.gitattributes b/sha1dc/.gitattributes
new file mode 100644 (file)
index 0000000..da53f40
--- /dev/null
@@ -0,0 +1 @@
+* whitespace=-indent-with-non-tab
index 35e9dd5bf44251d39a683f8e5bd7b9e23ca190e6..facea1bb560b57bbe7419dd1cce49f6b1172ddca 100644 (file)
@@ -5,9 +5,23 @@
 * https://opensource.org/licenses/MIT
 ***/
 
-#include "cache.h"
-#include "sha1dc/sha1.h"
-#include "sha1dc/ubc_check.h"
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <string.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#endif
+
+#ifdef SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_INCLUDE_SHA1_C
+#endif
+
+#ifndef SHA1DC_INIT_SAFE_HASH_DEFAULT
+#define SHA1DC_INIT_SAFE_HASH_DEFAULT 1
+#endif
+
+#include "sha1.h"
+#include "ubc_check.h"
 
 
 /*
    If you are compiling on a big endian platform and your compiler does not define one of these,
    you will have to add whatever macros your tool chain defines to indicate Big-Endianness.
  */
-#if (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \
-    (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) || \
-    defined(__BIG_ENDIAN__) || defined(__ARMEB__) || defined(__THUMBEB__) ||  defined(__AARCH64EB__) || \
-    defined(_MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__)
+#ifdef SHA1DC_BIGENDIAN
+#undef SHA1DC_BIGENDIAN
+#endif
+
+#if (defined(_BYTE_ORDER) || defined(__BYTE_ORDER) || defined(__BYTE_ORDER__))
+
+#if ((defined(_BYTE_ORDER) && (_BYTE_ORDER == _BIG_ENDIAN)) || \
+     (defined(__BYTE_ORDER) && (__BYTE_ORDER == __BIG_ENDIAN)) || \
+     (defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __BIG_ENDIAN__)) )
+#define SHA1DC_BIGENDIAN
+#endif
 
-#define SHA1DC_BIGENDIAN       1
 #else
+
+#if (defined(_BIG_ENDIAN) || defined(__BIG_ENDIAN) || defined(__BIG_ENDIAN__) || \
+     defined(__ARMEB__) || defined(__THUMBEB__) || defined(__AARCH64EB__) || \
+     defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || \
+     defined(__sparc))
+#define SHA1DC_BIGENDIAN
+#endif
+
+#endif
+
+#if (defined(SHA1DC_FORCE_LITTLEENDIAN) && defined(SHA1DC_BIGENDIAN))
 #undef SHA1DC_BIGENDIAN
-#endif /*ENDIANNESS SELECTION*/
+#endif
+#if (defined(SHA1DC_FORCE_BIGENDIAN) && !defined(SHA1DC_BIGENDIAN))
+#define SHA1DC_BIGENDIAN
+#endif
+/*ENDIANNESS SELECTION*/
+
+#if (defined SHA1DC_FORCE_UNALIGNED_ACCESS || \
+     defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || \
+     defined(i386) || defined(__i386) || defined(__i386__) || defined(__i486__)  || \
+     defined(__i586__) || defined(__i686__) || defined(_M_IX86) || defined(__X86__) || \
+     defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || defined(__INTEL__) || \
+     defined(__386) || defined(_M_X64) || defined(_M_AMD64))
+
+#define SHA1DC_ALLOW_UNALIGNED_ACCESS
+
+#endif /*UNALIGNMENT DETECTION*/
+
 
 #define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n))))
 #define rotate_left(x,n)  (((x)<<(n))|((x)>>(32-(n))))
 
 #define sha1_mix(W, t)  (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1))
 
-#if defined(SHA1DC_BIGENDIAN)
+#ifdef SHA1DC_BIGENDIAN
        #define sha1_load(m, t, temp)  { temp = m[t]; }
 #else
        #define sha1_load(m, t, temp)  { temp = m[t]; sha1_bswap32(temp); }
-#endif /* !defined(SHA1DC_BIGENDIAN) */
+#endif
 
 #define sha1_store(W, t, x)    *(volatile uint32_t *)&W[t] = x
 
@@ -869,6 +916,11 @@ static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], co
        ihvout[0] = ihvin[0] + a; ihvout[1] = ihvin[1] + b; ihvout[2] = ihvin[2] + c; ihvout[3] = ihvin[3] + d; ihvout[4] = ihvin[4] + e; \
 }
 
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable: 4127)  /* Complier complains about the checks in the above macro being constant. */
+#endif
+
 #ifdef DOSTORESTATE0
 SHA1_RECOMPRESS(0)
 #endif
@@ -1189,6 +1241,10 @@ SHA1_RECOMPRESS(78)
 SHA1_RECOMPRESS(79)
 #endif
 
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
 static void sha1_recompression_step(uint32_t step, uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5])
 {
        switch (step)
@@ -1662,7 +1718,7 @@ void SHA1DCInit(SHA1_CTX* ctx)
        ctx->ihv[3] = 0x10325476;
        ctx->ihv[4] = 0xC3D2E1F0;
        ctx->found_collision = 0;
-       ctx->safe_hash = 0;
+       ctx->safe_hash = SHA1DC_INIT_SAFE_HASH_DEFAULT;
        ctx->ubc_check = 1;
        ctx->detect_coll = 1;
        ctx->reduced_round_coll = 0;
@@ -1710,6 +1766,7 @@ void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback)
 void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len)
 {
        unsigned left, fill;
+
        if (len == 0)
                return;
 
@@ -1728,7 +1785,13 @@ void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len)
        while (len >= 64)
        {
                ctx->total += 64;
+
+#if defined(SHA1DC_ALLOW_UNALIGNED_ACCESS)
                sha1_process(ctx, (uint32_t*)(buf));
+#else
+               memcpy(ctx->buffer, buf, 64);
+               sha1_process(ctx, (uint32_t*)(ctx->buffer));
+#endif /* defined(SHA1DC_ALLOW_UNALIGNED_ACCESS) */
                buf += 64;
                len -= 64;
        }
@@ -1788,22 +1851,6 @@ int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx)
        return ctx->found_collision;
 }
 
-void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
-{
-       if (!SHA1DCFinal(hash, ctx))
-               return;
-       die("SHA-1 appears to be part of a collision attack: %s",
-           sha1_to_hex(hash));
-}
-
-void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, unsigned long len)
-{
-       const char *data = vdata;
-       /* We expect an unsigned long, but sha1dc only takes an int */
-       while (len > INT_MAX) {
-               SHA1DCUpdate(ctx, data, INT_MAX);
-               data += INT_MAX;
-               len -= INT_MAX;
-       }
-       SHA1DCUpdate(ctx, data, len);
-}
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C
+#endif
index bd8bd928fb3f28f7427f73e84f01fc62e1ba7138..1e4e94be54a6dd23fc29e5becf336ccc231d7ab3 100644 (file)
@@ -4,6 +4,7 @@
 * See accompanying file LICENSE.txt or copy at
 * https://opensource.org/licenses/MIT
 ***/
+
 #ifndef SHA1DC_SHA1_H
 #define SHA1DC_SHA1_H
 
 extern "C" {
 #endif
 
-/* uses SHA-1 message expansion to expand the first 16 words of W[] to 80 words */
-/* void sha1_message_expansion(uint32_t W[80]); */
-
-/* sha-1 compression function; first version takes a message block pre-parsed as 16 32-bit integers, second version takes an already expanded message) */
-/* void sha1_compression(uint32_t ihv[5], const uint32_t m[16]);
-void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80]); */
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
 
-/* same as sha1_compression_W, but additionally store intermediate states */
+/* sha-1 compression function that takes an already expanded message, and additionally store intermediate states */
 /* only stores states ii (the state between step ii-1 and step ii) when DOSTORESTATEii is defined in ubc_check.h */
 void sha1_compression_states(uint32_t[5], const uint32_t[16], uint32_t[80], uint32_t[80][5]);
 
 /*
-// function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5])
-// where 0 <= T < 80
-//       me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference)
-//       state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block
-// the function will return:
-//       ihvin: the reconstructed input chaining value
-//       ihvout: the reconstructed output chaining value
+// Function type for sha1_recompression_step_T (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]).
+// Where 0 <= T < 80
+//       me2 is an expanded message (the expansion of an original message block XOR'ed with a disturbance vector's message block difference.)
+//       state is the internal state (a,b,c,d,e) before step T of the SHA-1 compression function while processing the original message block.
+// The function will return:
+//       ihvin: The reconstructed input chaining value.
+//       ihvout: The reconstructed output chaining value.
 */
 typedef void(*sha1_recompression_type)(uint32_t*, uint32_t*, const uint32_t*, const uint32_t*);
 
-/* table of sha1_recompression_step_0, ... , sha1_recompression_step_79 */
-/* extern sha1_recompression_type sha1_recompression_step[80];*/
-
-/* a callback function type that can be set to be called when a collision block has been found: */
+/* A callback function type that can be set to be called when a collision block has been found: */
 /* void collision_block_callback(uint64_t byteoffset, const uint32_t ihvin1[5], const uint32_t ihvin2[5], const uint32_t m1[80], const uint32_t m2[80]) */
 typedef void(*collision_block_callback)(uint64_t, const uint32_t*, const uint32_t*, const uint32_t*, const uint32_t*);
 
-/* the SHA-1 context */
+/* The SHA-1 context. */
 typedef struct {
        uint64_t total;
        uint32_t ihv[5];
@@ -59,30 +54,34 @@ typedef struct {
        uint32_t states[80][5];
 } SHA1_CTX;
 
-/* initialize SHA-1 context */
+/* Initialize SHA-1 context. */
 void SHA1DCInit(SHA1_CTX*);
 
 /*
-// function to enable safe SHA-1 hashing:
-// collision attacks are thwarted by hashing a detected near-collision block 3 times
-// think of it as extending SHA-1 from 80-steps to 240-steps for such blocks:
-//   the best collision attacks against SHA-1 have complexity about 2^60,
-//   thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would 2^180
-//   an attacker would be better off using a generic birthday search of complexity 2^80
-//
-// enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected
-// but it will result in a different SHA-1 hash for messages where a collision attack was detected
-// this will automatically invalidate SHA-1 based digital signature forgeries
-// enabled by default
+    Function to enable safe SHA-1 hashing:
+    Collision attacks are thwarted by hashing a detected near-collision block 3 times.
+    Think of it as extending SHA-1 from 80-steps to 240-steps for such blocks:
+        The best collision attacks against SHA-1 have complexity about 2^60,
+        thus for 240-steps an immediate lower-bound for the best cryptanalytic attacks would be 2^180.
+        An attacker would be better off using a generic birthday search of complexity 2^80.
+
+   Enabling safe SHA-1 hashing will result in the correct SHA-1 hash for messages where no collision attack was detected,
+   but it will result in a different SHA-1 hash for messages where a collision attack was detected.
+   This will automatically invalidate SHA-1 based digital signature forgeries.
+   Enabled by default.
 */
 void SHA1DCSetSafeHash(SHA1_CTX*, int);
 
-/* function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up) */
-/* enabled by default */
+/*
+    Function to disable or enable the use of Unavoidable Bitconditions (provides a significant speed up).
+    Enabled by default
+ */
 void SHA1DCSetUseUBC(SHA1_CTX*, int);
 
-/* function to disable or enable the use of Collision Detection */
-/* enabled by default */
+/*
+    Function to disable or enable the use of Collision Detection.
+    Enabled by default.
+ */
 void SHA1DCSetUseDetectColl(SHA1_CTX*, int);
 
 /* function to disable or enable the detection of reduced-round SHA-1 collisions */
@@ -100,23 +99,12 @@ void SHA1DCUpdate(SHA1_CTX*, const char*, size_t);
 /* returns: 0 = no collision detected, otherwise = collision found => warn user for active attack */
 int  SHA1DCFinal(unsigned char[20], SHA1_CTX*);
 
-/*
- * Same as SHA1DCFinal, but convert collision attack case into a verbose die().
- */
-void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
-
-/*
- * Same as SHA1DCUpdate, but adjust types to match git's usual interface.
- */
-void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len);
-
-#define platform_SHA_CTX SHA1_CTX
-#define platform_SHA1_Init SHA1DCInit
-#define platform_SHA1_Update git_SHA1DCUpdate
-#define platform_SHA1_Final git_SHA1DCFinal
-
 #if defined(__cplusplus)
 }
 #endif
 
-#endif /* SHA1DC_SHA1_H */
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H
+#endif
+
+#endif
index 089dd4743d8bc1c9827e5ce3ca90541d10efbb41..b3beff2afbae0e3ca338bedf20c8b16806fe53ac 100644 (file)
 // ubc_check has been verified against ubc_check_verify using the 'ubc_check_test' program in the tools section
 */
 
-#include "git-compat-util.h"
-#include "sha1dc/ubc_check.h"
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+#ifdef SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_INCLUDE_UBC_CHECK_C
+#endif
+#include "ubc_check.h"
 
 static const uint32_t DV_I_43_0_bit    = (uint32_t)(1) << 0;
 static const uint32_t DV_I_44_0_bit    = (uint32_t)(1) << 1;
@@ -361,3 +366,7 @@ if (mask) {
 
        dvmask[0]=mask;
 }
+
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_C
+#endif
index b64c306d77ff776acc6630bb7a855098e4ccd939..d7e17dc734d07703a9b6f6b01a44d282c11e711b 100644 (file)
 // thus one needs to do the recompression check for each DV that has its bit set
 */
 
-#ifndef UBC_CHECK_H
-#define UBC_CHECK_H
+#ifndef SHA1DC_UBC_CHECK_H
+#define SHA1DC_UBC_CHECK_H
 
 #if defined(__cplusplus)
 extern "C" {
 #endif
 
+#ifndef SHA1DC_NO_STANDARD_INCLUDES
+#include <stdint.h>
+#endif
+
 #define DVMASKSIZE 1
 typedef struct { int dvType; int dvK; int dvB; int testt; int maski; int maskb; uint32_t dm[80]; } dv_info_t;
 extern dv_info_t sha1_dvs[];
@@ -41,4 +45,8 @@ void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]);
 }
 #endif
 
-#endif /* UBC_CHECK_H */
+#ifdef SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#include SHA1DC_CUSTOM_TRAILING_INCLUDE_UBC_CHECK_H
+#endif
+
+#endif
diff --git a/sha1dc_git.c b/sha1dc_git.c
new file mode 100644 (file)
index 0000000..4d32b4f
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * This code is included at the end of sha1dc/sha1.c with the
+ * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_C macro.
+ */
+
+void git_SHA1DCFinal(unsigned char hash[20], SHA1_CTX *ctx)
+{
+       if (!SHA1DCFinal(hash, ctx))
+               return;
+       die("SHA-1 appears to be part of a collision attack: %s",
+           sha1_to_hex(hash));
+}
+
+void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *vdata, unsigned long len)
+{
+       const char *data = vdata;
+       /* We expect an unsigned long, but sha1dc only takes an int */
+       while (len > INT_MAX) {
+               SHA1DCUpdate(ctx, data, INT_MAX);
+               data += INT_MAX;
+               len -= INT_MAX;
+       }
+       SHA1DCUpdate(ctx, data, len);
+}
diff --git a/sha1dc_git.h b/sha1dc_git.h
new file mode 100644 (file)
index 0000000..a8a5c1d
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * This code is included at the end of sha1dc/sha1.h with the
+ * SHA1DC_CUSTOM_TRAILING_INCLUDE_SHA1_H macro.
+ */
+
+/*
+ * Same as SHA1DCFinal, but convert collision attack case into a verbose die().
+ */
+void git_SHA1DCFinal(unsigned char [20], SHA1_CTX *);
+
+/*
+ * Same as SHA1DCUpdate, but adjust types to match git's usual interface.
+ */
+void git_SHA1DCUpdate(SHA1_CTX *ctx, const void *data, unsigned long len);
+
+#define platform_SHA_CTX SHA1_CTX
+#define platform_SHA1_Init SHA1DCInit
+#define platform_SHA1_Update git_SHA1DCUpdate
+#define platform_SHA1_Final git_SHA1DCFinal
index 25b6db989bf4ab475bd7fa224c63b753d61f586e..ef7ca78993df20082c858a7d2b8c7094fcbd1590 100644 (file)
--- 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,18 +466,22 @@ 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);
-       uint32_t *tmp = xmalloc(bitmap_size); /* to be freed before return */
-       uint32_t *bitmap = paint_alloc(info);
-       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;
+
        if (!c)
                return;
+
+       tmp = xmalloc(bitmap_size);
+       bitmap = paint_alloc(info);
        memset(bitmap, 0, bitmap_size);
        bitmap[id / 32] |= (1U << (id % 32));
        commit_list_insert(c, &head);
@@ -531,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;
@@ -599,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);
@@ -641,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;
@@ -679,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;
@@ -700,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;
@@ -722,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;
index f519e60f87578386ec9b4ea764bf530be0f64836..49bd197f715ae783d7fdbe8e1d638a1bcac0073c 100644 (file)
@@ -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;
 }
index df91c1bda8117fe7d0f25a0aaedce79370801ce0..65c0f09b2bd413e091cc65ef2f0d3a2d92aeb858 100644 (file)
@@ -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
index 003ca1879ef560b3db0ad9e1af8022c38800b500..c650500c6e51d983dc45a4be3b8dad98f1dece92 100644 (file)
@@ -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;
index d3809a14172da5b1512b08fa411136821465659e..29bfb7ae45931686e14c91b1a47a703e7b32fe52 100644 (file)
@@ -62,6 +62,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 (file)
index 0000000..92f8aea
--- /dev/null
@@ -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 (file)
index 0000000..7d451e1
--- /dev/null
@@ -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
index d3299e29c0855b8503f444f659e2b24bba8ecb1f..1b8a3b575db6c85a42e9c9e285617113ecac4a84 100644 (file)
 #include "quote.h"
 #include "remote.h"
 #include "worktree.h"
+#include "parse-options.h"
 
 static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
-static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
+static int config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
 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;
@@ -153,7 +154,8 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,
        }
 }
 
-int submodule_config(const char *var, const char *value, void *cb)
+/* For loading from the .gitmodules file. */
+static int git_modules_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "submodule.fetchjobs")) {
                parallel_jobs = git_config_int(var, value);
@@ -169,6 +171,56 @@ int submodule_config(const char *var, const char *value, void *cb)
        return 0;
 }
 
+/* Loads all submodule settings from the config. */
+int submodule_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "submodule.recurse")) {
+               int v = git_config_bool(var, value) ?
+                       RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
+               config_update_recurse_submodules = v;
+               return 0;
+       } else {
+               return git_modules_config(var, value, cb);
+       }
+}
+
+/* Cheap function that only determines if we're interested in submodules at all */
+int git_default_submodule_config(const char *var, const char *value, void *cb)
+{
+       if (!strcmp(var, "submodule.recurse")) {
+               int v = git_config_bool(var, value) ?
+                       RECURSE_SUBMODULES_ON : RECURSE_SUBMODULES_OFF;
+               config_update_recurse_submodules = v;
+       }
+       return 0;
+}
+
+int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
+                                                    const char *arg, int unset)
+{
+       if (unset) {
+               config_update_recurse_submodules = RECURSE_SUBMODULES_OFF;
+               return 0;
+       }
+       if (arg)
+               config_update_recurse_submodules =
+                       parse_update_recurse_submodules_arg(opt->long_name,
+                                                           arg);
+       else
+               config_update_recurse_submodules = RECURSE_SUBMODULES_ON;
+
+       return 0;
+}
+
+void load_submodule_cache(void)
+{
+       if (config_update_recurse_submodules == RECURSE_SUBMODULES_OFF)
+               return;
+
+       gitmodules_config();
+       git_config(submodule_config, NULL);
+}
+
 void gitmodules_config(void)
 {
        const char *work_tree = get_git_work_tree();
@@ -196,7 +248,8 @@ void gitmodules_config(void)
                }
 
                if (!gitmodules_is_unmerged)
-                       git_config_from_file(submodule_config, gitmodules_path.buf, NULL);
+                       git_config_from_file(git_modules_config,
+                               gitmodules_path.buf, NULL);
                strbuf_release(&gitmodules_path);
        }
 }
@@ -207,7 +260,7 @@ void gitmodules_config_sha1(const unsigned char *commit_sha1)
        unsigned char sha1[20];
 
        if (gitmodule_sha1_from_commit(commit_sha1, sha1, &rev)) {
-               git_config_from_blob_sha1(submodule_config, rev.buf,
+               git_config_from_blob_sha1(git_modules_config, rev.buf,
                                          sha1, NULL);
        }
        strbuf_release(&rev);
@@ -282,6 +335,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 +563,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 +670,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/",
@@ -596,11 +713,6 @@ void set_config_fetch_recurse_submodules(int value)
        config_fetch_recurse_submodules = value;
 }
 
-void set_config_update_recurse_submodules(int value)
-{
-       config_update_recurse_submodules = value;
-}
-
 int should_update_submodules(void)
 {
        return config_update_recurse_submodules == RECURSE_SUBMODULES_ON;
@@ -617,6 +729,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 +834,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 +844,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 +929,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 +1070,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)
-{
-       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)
+static int append_oid_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 +1468,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 +1485,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 +1514,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 +1549,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 +1574,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 +1596,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 +1695,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 +1706,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 +1737,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;
        }
 
index 1277480add48140c6bf8c6cbc51cb962e27637ae..cbe5c1726f9adab2b032d872d4bd84425e6ce518 100644 (file)
@@ -39,6 +39,12 @@ extern void stage_updated_gitmodules(void);
 extern void set_diffopt_flags_from_submodule_config(struct diff_options *,
                const char *path);
 extern int submodule_config(const char *var, const char *value, void *cb);
+extern int git_default_submodule_config(const char *var, const char *value, void *cb);
+
+struct option;
+int option_parse_recurse_submodules_worktree_updater(const struct option *opt,
+                                                    const char *arg, int unset);
+void load_submodule_cache(void);
 extern void gitmodules_config(void);
 extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
 extern int is_submodule_initialized(const char *path);
@@ -49,6 +55,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);
@@ -65,7 +75,6 @@ extern void show_submodule_inline_diff(FILE *f, const char *path,
                const char *del, const char *add, const char *reset,
                const struct diff_options *opt);
 extern void set_config_fetch_recurse_submodules(int value);
-extern void set_config_update_recurse_submodules(int value);
 /* Check if we want to update any submodule.*/
 extern int should_update_submodules(void);
 /*
@@ -84,10 +93,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);
index 2d44088f56e56fadae0eb9c8fe69ad05c1bbc441..3bd959ae523cff7fe3a29f1abb92019b6d10515f 100644 (file)
@@ -1,2 +1,22 @@
 t[0-9][0-9][0-9][0-9]/* -whitespace
-t0110/url-* binary
+/diff-lib/* eol=lf
+/t0110/url-* binary
+/t3900/*.txt eol=lf
+/t3901/*.txt eol=lf
+/t4034/*/* eol=lf
+/t4013/* eol=lf
+/t4018/* eol=lf
+/t4051/* eol=lf
+/t4100/* eol=lf
+/t4101/* eol=lf
+/t4109/* eol=lf
+/t4110/* eol=lf
+/t4135/* eol=lf
+/t4211/* eol=lf
+/t4252/* eol=lf
+/t5100/* eol=lf
+/t5515/* eol=lf
+/t556x_common eol=lf
+/t7500/* eol=lf
+/t8005/*.txt eol=lf
+/t9*/*.dump eol=lf
index ab386c36818890a085d8917102b8f8a479ff34cd..2f958603697515f40520a7ff9df31de13eefc2d7 100644 (file)
--- 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
 ----------------------
 
index 506054bcd5dfbd76c8aec85382f35794514b9db9..f414a3ac670fb2d77645f5d7301b6aa8f603379e 100644 (file)
@@ -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;
index 7af116d49e04777b9321044e199254a2211aa4d4..ebf3aab22d6c197b99b0203dbe9881ccf417be86 100644 (file)
@@ -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 */
index e9395028630bf26390366065aa07bc7b2827eb73..356d8edef1d25524abaae0f0fcb3bf072d229c30 100644 (file)
@@ -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]);
 
index a01430c24bdb8c0e35147b71d6f9e898405b1c85..75fe883aac1ee17b9dda40dd88b7a4a4fd328318 100644 (file)
@@ -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);
index 2d84c45ffe99d1d5430345a32d1386804c3c495d..05d8c4d8af093ad42d855b17d243fe07b8893bfb 100644 (file)
@@ -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;
index fb4f7b014e10cd383ed42550f77a14ea9b0211ac..2d26f86800906ab1783edf10e7196adadd5f4af7 100755 (executable)
@@ -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 &&
@@ -780,18 +781,14 @@ test_submodule_forced_switch () {
 # - Removing a submodule with a git directory absorbs the submodules
 #   git directory first into the superproject.
 
-test_submodule_switch_recursing () {
-       command="$1"
+test_submodule_switch_recursing_with_args () {
+       cmd_args="$1"
+       command="git $cmd_args --recurse-submodules"
        RESULTDS=success
        if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
        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
@@ -988,6 +985,18 @@ test_submodule_switch_recursing () {
                )
        '
 
+       test_expect_success "git -c submodule.recurse=true $cmd_args: modified submodule updates submodule work tree" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t modify_sub1 origin/modify_sub1 &&
+                       git -c submodule.recurse=true $cmd_args modify_sub1 &&
+                       test_superproject_content origin/modify_sub1 &&
+                       test_submodule_content sub1 origin/modify_sub1
+               )
+       '
+
        # Updating a submodule to an invalid sha1 doesn't update the
        # superproject nor the submodule's work tree.
        test_expect_success "$command: updating to a missing submodule commit fails" '
@@ -1003,7 +1012,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 &&
                (
@@ -1020,8 +1029,9 @@ test_submodule_switch_recursing () {
 # Test that submodule contents are updated when switching between commits
 # that change a submodule, but throwing away local changes in
 # the superproject as well as the submodule is allowed.
-test_submodule_forced_switch_recursing () {
-       command="$1"
+test_submodule_forced_switch_recursing_with_args () {
+       cmd_args="$1"
+       command="git $cmd_args --recurse-submodules"
        RESULT=success
        if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
        then
@@ -1212,14 +1222,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
                )
        '
 }
index 49ea4349bedfc53b5b565cf1102c74ccb93ab653..21321a0f361203aacc78516ae25f21f35107da02 100644 (file)
@@ -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
 
index 5afa8c8df3b4dd9f6bae4b1f46351f9ed04fa566..8de5a98cfc6c513bb35cd8355f3e831ab3a8007d 100755 (executable)
@@ -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 (executable)
index 0000000..dd18a9c
--- /dev/null
@@ -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
index b3e7d525d277c339b4f9d821fb6d18dfb2aa2621..ce271ca4c1a07f620f25c5f4dd6febe020308c64 100755 (executable)
@@ -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 (executable)
index 0000000..2bc47de
--- /dev/null
@@ -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 (executable)
index 0000000..0609712
--- /dev/null
@@ -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 (executable)
index 0000000..62aba19
--- /dev/null
@@ -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 (executable)
index 0000000..c7ef1e1
--- /dev/null
@@ -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
index ab4b8b06ae50c5e4ee7e128ded55dea7dddf7865..b6fc880395791c6f33577dc83387939ceb786bbf 100644 (file)
@@ -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"
 }
index c788d713ae927007866b5b73920de2674f816207..beb4acc0e428d20280a649c428da8b0b3aa5b4c6 100755 (executable)
@@ -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 () {
index c0c910867d75368832ce8b297e9dd82ee984a85a..42d4ea61ef531d8c495c5c2216d434982f1c6182 100755 (executable)
@@ -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/t0025-crlf-auto.sh b/t/t0025-crlf-auto.sh
deleted file mode 100755 (executable)
index 89826c5..0000000
+++ /dev/null
@@ -1,181 +0,0 @@
-#!/bin/sh
-
-test_description='CRLF conversion'
-
-. ./test-lib.sh
-
-has_cr() {
-       tr '\015' Q <"$1" | grep Q >/dev/null
-}
-
-test_expect_success setup '
-
-       git config core.autocrlf false &&
-
-       for w in Hello world how are you; do echo $w; done >LFonly &&
-       for w in I am very very fine thank you; do echo ${w}Q; done | q_to_cr >CRLFonly &&
-       for w in Oh here is a QNUL byte how alarming; do echo ${w}; done | q_to_nul >LFwithNUL &&
-       git add . &&
-
-       git commit -m initial &&
-
-       LFonly=$(git rev-parse HEAD:LFonly) &&
-       CRLFonly=$(git rev-parse HEAD:CRLFonly) &&
-       LFwithNUL=$(git rev-parse HEAD:LFwithNUL) &&
-
-       echo happy.
-'
-
-test_expect_success 'default settings cause no changes' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       git read-tree --reset -u HEAD &&
-
-       ! has_cr LFonly &&
-       has_cr CRLFonly &&
-       LFonlydiff=$(git diff LFonly) &&
-       CRLFonlydiff=$(git diff CRLFonly) &&
-       LFwithNULdiff=$(git diff LFwithNUL) &&
-       test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
-'
-
-test_expect_success 'crlf=true causes a CRLF file to be normalized' '
-
-       # Backwards compatibility check
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       echo "CRLFonly crlf" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       # Note, "normalized" means that git will normalize it if added
-       has_cr CRLFonly &&
-       CRLFonlydiff=$(git diff CRLFonly) &&
-       test -n "$CRLFonlydiff"
-'
-
-test_expect_success 'text=true causes a CRLF file to be normalized' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       echo "CRLFonly text" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       # Note, "normalized" means that git will normalize it if added
-       has_cr CRLFonly &&
-       CRLFonlydiff=$(git diff CRLFonly) &&
-       test -n "$CRLFonlydiff"
-'
-
-test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=false' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       git config core.autocrlf false &&
-       echo "LFonly eol=crlf" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       has_cr LFonly &&
-       LFonlydiff=$(git diff LFonly) &&
-       test -z "$LFonlydiff"
-'
-
-test_expect_success 'eol=crlf gives a normalized file CRLFs with autocrlf=input' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       git config core.autocrlf input &&
-       echo "LFonly eol=crlf" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       has_cr LFonly &&
-       LFonlydiff=$(git diff LFonly) &&
-       test -z "$LFonlydiff"
-'
-
-test_expect_success 'eol=lf gives a normalized file LFs with autocrlf=true' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       git config core.autocrlf true &&
-       echo "LFonly eol=lf" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       ! has_cr LFonly &&
-       LFonlydiff=$(git diff LFonly) &&
-       test -z "$LFonlydiff"
-'
-
-test_expect_success 'autocrlf=true does not normalize CRLF files' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       git config core.autocrlf true &&
-       git read-tree --reset -u HEAD &&
-
-       has_cr LFonly &&
-       has_cr CRLFonly &&
-       LFonlydiff=$(git diff LFonly) &&
-       CRLFonlydiff=$(git diff CRLFonly) &&
-       LFwithNULdiff=$(git diff LFwithNUL) &&
-       test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
-'
-
-test_expect_success 'text=auto, autocrlf=true does not normalize CRLF files' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       git config core.autocrlf true &&
-       echo "* text=auto" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       has_cr LFonly &&
-       has_cr CRLFonly &&
-       LFonlydiff=$(git diff LFonly) &&
-       CRLFonlydiff=$(git diff CRLFonly) &&
-       LFwithNULdiff=$(git diff LFwithNUL) &&
-       test -z "$LFonlydiff" -a -z "$CRLFonlydiff" -a -z "$LFwithNULdiff"
-'
-
-test_expect_success 'text=auto, autocrlf=true does not normalize binary files' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       git config core.autocrlf true &&
-       echo "* text=auto" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       ! has_cr LFwithNUL &&
-       LFwithNULdiff=$(git diff LFwithNUL) &&
-       test -z "$LFwithNULdiff"
-'
-
-test_expect_success 'eol=crlf _does_ normalize binary files' '
-
-       rm -f .gitattributes tmp LFonly CRLFonly LFwithNUL &&
-       echo "LFwithNUL eol=crlf" > .gitattributes &&
-       git read-tree --reset -u HEAD &&
-
-       has_cr LFwithNUL &&
-       LFwithNULdiff=$(git diff LFwithNUL) &&
-       test -z "$LFwithNULdiff"
-'
-
-test_expect_success 'prepare unnormalized' '
-       > .gitattributes &&
-       git config core.autocrlf false &&
-       printf "LINEONE\nLINETWO\r\n"     >mixed &&
-       git add mixed .gitattributes &&
-       git commit -m "Add mixed" &&
-       git ls-files --eol | egrep "i/crlf" &&
-       git ls-files --eol | egrep "i/mixed"
-'
-
-test_expect_success 'normalize unnormalized' '
-       echo "* text=auto" >.gitattributes &&
-       rm .git/index &&
-       git add . &&
-       git commit -m "Introduce end-of-line normalization" &&
-       git ls-files --eol | tr "\\t" " " | sort >act &&
-cat >exp <<EOF &&
-i/-text w/-text attr/text=auto         LFwithNUL
-i/lf    w/crlf  attr/text=auto         CRLFonly
-i/lf    w/crlf  attr/text=auto         LFonly
-i/lf    w/lf    attr/text=auto         .gitattributes
-i/lf    w/mixed attr/text=auto         mixed
-EOF
-       test_cmp exp act
-'
-
-test_done
index 90db54c9f976c3c7b1d1d1a55bc3be6e557f2aa2..deb3ae7813052d01b6dab92586e2c37d313ef8ff 100755 (executable)
@@ -4,12 +4,6 @@ test_description='CRLF conversion all combinations'
 
 . ./test-lib.sh
 
-if ! test_have_prereq EXPENSIVE
-then
-       skip_all="EXPENSIVE not set"
-       test_done
-fi
-
 compare_files () {
        tr '\015\000' QN <"$1" >"$1".expect &&
        tr '\015\000' QN <"$2" | tr -d 'Z' >"$2".actual &&
@@ -75,7 +69,7 @@ check_warning () {
        *) echo >&2 "Illegal 1": "$1" ; return false ;;
        esac
        grep "will be replaced by" "$2" | sed -e "s/\(.*\) in [^ ]*$/\1/" | uniq  >"$2".actual
-       test_cmp "$2".expect "$2".actual
+       test_i18ncmp "$2".expect "$2".actual
 }
 
 commit_check_warn () {
index 12228b4aa6cb6a393a006130b31c73625911cec6..e4739170aa2b7c833cd51a06a7ac6764c8bf0494 100755 (executable)
@@ -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 &&
index a2124600811618efc2c79d7a3b8d04c9bb838fc8..71b0d74b4ddef584e3f8af711df6b5c5678034c5 100755 (executable)
@@ -8,7 +8,7 @@ test_description="The Git C functions aren't broken by setlocale(3)"
 . ./lib-gettext.sh
 
 test_expect_success 'git show a ISO-8859-1 commit under C locale' '
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
        test_commit "iso-c-commit" iso-under-c &&
        git show >out 2>err &&
        ! test -s err &&
@@ -16,7 +16,7 @@ test_expect_success 'git show a ISO-8859-1 commit under C locale' '
 '
 
 test_expect_success GETTEXT_LOCALE 'git show a ISO-8859-1 commit under a UTF-8 locale' '
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
        test_commit "iso-utf8-commit" iso-under-utf8 &&
        LANGUAGE=is LC_ALL="$is_IS_locale" git show >out 2>err &&
        ! test -s err &&
index de1ba02dc5b1c76ea52eb490298bcfcefc342b51..91a6fafcb425f367192c7eb4e4cdf0ae4d19ac37 100755 (executable)
@@ -5,13 +5,12 @@ 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
 
-test_submodule_switch_recursing "git read-tree --recurse-submodules -u -m"
+test_submodule_switch_recursing_with_args "read-tree -u -m"
 
-test_submodule_forced_switch_recursing "git read-tree --recurse-submodules -u --reset"
+test_submodule_forced_switch_recursing_with_args "read-tree -u --reset"
 
 test_submodule_switch "git read-tree -u -m"
 
index afcca0d52c377cfabd6463f060d1b183937ba9de..13b7851f7c2feb87f63449d917380452e85c1f84 100755 (executable)
@@ -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
index 933915ec06ec725f7cf6a9ed3e251f6ff2390f76..d9d2f545a4ed735e02f7d5cd6ceee7d873fdec94 100755 (executable)
@@ -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
index 1af8c454cfa715d5694c16b4848428ae0b75eda6..3dda215e8e2f37c049a3169cecdb3e43ddea5dfb 100755 (executable)
@@ -77,7 +77,7 @@ test_with_config () {
 
 test_expect_success 'ignore .git/ with incompatible repository version' '
        test_with_config "[core]repositoryformatversion = 999999" 2>err &&
-       grep "warning:.* Expected git repo version <= [1-9]" err
+       test_i18ngrep "warning:.* Expected git repo version <= [1-9]" err
 '
 
 test_expect_failure 'ignore .git/ with invalid repository version' '
index 490521f8cbd5eb28607b38860fb19a3ee20fdd56..e8115df5bad8d9ef46542cfd3c96c32da0bfa4ad 100755 (executable)
@@ -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 --
 '
index 13b5454c5642f2f21786bb8bd3d06a2d27f1cf15..c32d4cc4652a4496ce8fa6aa1c10c797ae7d760a 100755 (executable)
@@ -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 (executable)
index 0000000..5df06f3
--- /dev/null
@@ -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
index 8937e25e4955b79d72952289e7485652309def3a..e88349c8a0483b97ea38013de61d799003fd85b5 100755 (executable)
@@ -122,7 +122,7 @@ test_expect_success 'push cannot create a badly named ref' '
        ! grep -e "broken\.\.\.ref" output
 '
 
-test_expect_failure 'push --mirror can delete badly named ref' '
+test_expect_failure C_LOCALE_OUTPUT 'push --mirror can delete badly named ref' '
        top=$(pwd) &&
        git init src &&
        git init dest &&
diff --git a/t/t1601-index-bogus.sh b/t/t1601-index-bogus.sh
new file mode 100755 (executable)
index 0000000..73cc932
--- /dev/null
@@ -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
index e8f70b806f110c75b9bda5bca0af6732be391fcd..6ef15738e44ed8ad82960c042b3212fce266ec93 100755 (executable)
@@ -64,10 +64,9 @@ 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_switch_recursing_with_args "checkout"
 
-test_submodule_forced_switch_recursing "git checkout -f --recurse-submodules"
+test_submodule_forced_switch_recursing_with_args "checkout -f"
 
 test_submodule_switch "git checkout"
 
index ef509df351600f1238eab243ec353ab73ec8518f..7ca69f4bed6438009808f35c2e212b269bffcfb0 100755 (executable)
@@ -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]'
index fe62e7c775da6a8fc191ed1dc6634d5285ddbc4b..10f8f026ffb4b21e62bdfa5fe1b8a4d6a3b3f13c 100755 (executable)
@@ -338,7 +338,7 @@ test_expect_success 'git branch -m s/s s should work when s/t is deleted' '
 
 test_expect_success 'config information was renamed, too' '
        test $(git config branch.s.dummy) = Hello &&
-       test_must_fail git config branch.s/s/dummy
+       test_must_fail git config branch.s/s.dummy
 '
 
 test_expect_success 'deleting a symref' '
index 5778c0afe12be2495b58a5251fb9bc8dc395a143..a428ae670369505476ab19338bf0f1da70018301 100755 (executable)
@@ -236,7 +236,7 @@ test_expect_success 'git branch --format option' '
        Refname is refs/heads/ref-to-remote
        EOF
        git branch --format="Refname is %(refname)" >actual &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 test_done
index 33d392ba112e2c130a02d2229967842ca257e837..5bd0275930b715c25507fc9744c8946e7f73900b 100755 (executable)
@@ -366,7 +366,7 @@ test_expect_success 'verbose flag is heeded, even after --continue' '
        grep "^ file1 | 2 +-$" output
 '
 
-test_expect_success 'multi-squash only fires up editor once' '
+test_expect_success C_LOCALE_OUTPUT 'multi-squash only fires up editor once' '
        base=$(git rev-parse HEAD~4) &&
        set_fake_editor &&
        FAKE_COMMIT_AMEND="ONCE" FAKE_LINES="1 squash 2 squash 3 squash 4" \
@@ -376,7 +376,7 @@ test_expect_success 'multi-squash only fires up editor once' '
        test 1 = $(git show | grep ONCE | wc -l)
 '
 
-test_expect_success 'multi-fixup does not fire up editor' '
+test_expect_success C_LOCALE_OUTPUT 'multi-fixup does not fire up editor' '
        git checkout -b multi-fixup E &&
        base=$(git rev-parse HEAD~4) &&
        set_fake_editor &&
@@ -426,7 +426,7 @@ D
 ONCE
 EOF
 
-test_expect_success 'squash and fixup generate correct log messages' '
+test_expect_success C_LOCALE_OUTPUT 'squash and fixup generate correct log messages' '
        git checkout -b squash-fixup E &&
        base=$(git rev-parse HEAD~4) &&
        set_fake_editor &&
@@ -439,7 +439,7 @@ test_expect_success 'squash and fixup generate correct log messages' '
        git branch -D squash-fixup
 '
 
-test_expect_success 'squash ignores comments' '
+test_expect_success C_LOCALE_OUTPUT 'squash ignores comments' '
        git checkout -b skip-comments E &&
        base=$(git rev-parse HEAD~4) &&
        set_fake_editor &&
@@ -452,7 +452,7 @@ test_expect_success 'squash ignores comments' '
        git branch -D skip-comments
 '
 
-test_expect_success 'squash ignores blank lines' '
+test_expect_success C_LOCALE_OUTPUT 'squash ignores blank lines' '
        git checkout -b skip-blank-lines E &&
        base=$(git rev-parse HEAD~4) &&
        set_fake_editor &&
@@ -860,7 +860,7 @@ test_expect_success 'rebase -ix with several instances of --exec' '
        test_cmp expected actual
 '
 
-test_expect_success 'rebase -ix with --autosquash' '
+test_expect_success C_LOCALE_OUTPUT 'rebase -ix with --autosquash' '
        git reset --hard execute &&
        git checkout -b autosquash &&
        echo second >second.txt &&
@@ -943,7 +943,7 @@ test_expect_success 'rebase -i --root fixup root commit' '
        test 0 = $(git cat-file commit HEAD | grep -c ^parent\ )
 '
 
-test_expect_success 'rebase --edit-todo does not works on non-interactive rebase' '
+test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-interactive rebase' '
        git reset --hard &&
        git checkout conflict-branch &&
        set_fake_editor &&
index 48346f1cc0c12883029349e4999d7e6697b35adb..5848949ec3700c2188d581500c19eaffa1392c3d 100755 (executable)
@@ -234,23 +234,23 @@ test_auto_fixup_fixup () {
        fi
 }
 
-test_expect_success 'fixup! fixup!' '
+test_expect_success C_LOCALE_OUTPUT 'fixup! fixup!' '
        test_auto_fixup_fixup fixup fixup
 '
 
-test_expect_success 'fixup! squash!' '
+test_expect_success C_LOCALE_OUTPUT 'fixup! squash!' '
        test_auto_fixup_fixup fixup squash
 '
 
-test_expect_success 'squash! squash!' '
+test_expect_success C_LOCALE_OUTPUT 'squash! squash!' '
        test_auto_fixup_fixup squash squash
 '
 
-test_expect_success 'squash! fixup!' '
+test_expect_success C_LOCALE_OUTPUT 'squash! fixup!' '
        test_auto_fixup_fixup squash fixup
 '
 
-test_expect_success 'autosquash with custom inst format' '
+test_expect_success C_LOCALE_OUTPUT 'autosquash with custom inst format' '
        git reset --hard base &&
        git config --add rebase.instructionFormat "[%an @ %ar] %s"  &&
        echo 2 >file1 &&
index bf0a5c9887235afb5e336a34815d6bb3d64875d8..9888bf34b9538f6e4aae1a1c65022ae126a62ce2 100755 (executable)
@@ -208,6 +208,50 @@ test_expect_success 'cherry-pick -x -s adds sob even when trailing sob exists fo
        test_cmp expect actual
 '
 
+test_expect_success 'cherry-pick -x handles commits with no NL at end of message' '
+       pristine_detach initial &&
+       printf "title\n\nSigned-off-by: A <a@example.com>" >msg &&
+       sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
+       git cherry-pick -x $sha1 &&
+       git log -1 --pretty=format:%B >actual &&
+
+       printf "\n(cherry picked from commit %s)\n" $sha1 >>msg &&
+       test_cmp msg actual
+'
+
+test_expect_success 'cherry-pick -x handles commits with no footer and no NL at end of message' '
+       pristine_detach initial &&
+       printf "title\n\nnot a footer" >msg &&
+       sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
+       git cherry-pick -x $sha1 &&
+       git log -1 --pretty=format:%B >actual &&
+
+       printf "\n\n(cherry picked from commit %s)\n" $sha1 >>msg &&
+       test_cmp msg actual
+'
+
+test_expect_success 'cherry-pick -s handles commits with no NL at end of message' '
+       pristine_detach initial &&
+       printf "title\n\nSigned-off-by: A <a@example.com>" >msg &&
+       sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
+       git cherry-pick -s $sha1 &&
+       git log -1 --pretty=format:%B >actual &&
+
+       printf "\nSigned-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n" >>msg &&
+       test_cmp msg actual
+'
+
+test_expect_success 'cherry-pick -s handles commits with no footer and no NL at end of message' '
+       pristine_detach initial &&
+       printf "title\n\nnot a footer" >msg &&
+       sha1=$(git commit-tree -p initial mesg-with-footer^{tree} <msg) &&
+       git cherry-pick -s $sha1 &&
+       git log -1 --pretty=format:%B >actual &&
+
+       printf "\n\nSigned-off-by: $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL>\n" >>msg &&
+       test_cmp msg actual
+'
+
 test_expect_success 'cherry-pick -x treats "(cherry picked from..." line as part of footer' '
        pristine_detach initial &&
        sha1=$(git rev-parse mesg-with-cherry-footer^0) &&
index 5f9913ba33d3edc848b03fc37bed587fe5c54849..f8568f8841d34d17f3d8fdae4d8d2404a6693c4d 100755 (executable)
@@ -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/t3901-8859-1.txt b/t/t3901-8859-1.txt
deleted file mode 100755 (executable)
index 38c21a6..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-: to be sourced in t3901 -- this is latin-1
-GIT_AUTHOR_NAME="Áéí óú" &&
-GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
-export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
index f663d567c8ac89e59236df79a632cbd2609910f5..923eb01f0ea4bec0d3a7d96a2492a0755ad00ef9 100755 (executable)
@@ -31,7 +31,7 @@ test_expect_success setup '
 
        # use UTF-8 in author and committer name to match the
        # i18n.commitencoding settings
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        test_tick &&
        echo "$GIT_AUTHOR_NAME" >mine &&
@@ -55,7 +55,7 @@ test_expect_success setup '
                # the second one on the side branch is ISO-8859-1
                git config i18n.commitencoding ISO8859-1 &&
                # use author and committer name in ISO-8859-1 to match it.
-               . "$TEST_DIRECTORY"/t3901-8859-1.txt
+               . "$TEST_DIRECTORY"/t3901/8859-1.txt
        fi &&
        test_tick &&
        echo Yet another >theirs &&
@@ -100,7 +100,7 @@ test_expect_success 'rebase (U/U)' '
 
        # The result will be committed by GIT_COMMITTER_NAME --
        # we want UTF-8 encoded name.
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
        git checkout -b test &&
        git rebase master &&
 
@@ -110,7 +110,7 @@ test_expect_success 'rebase (U/U)' '
 test_expect_success 'rebase (U/L)' '
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        git reset --hard side &&
        git rebase master &&
@@ -122,7 +122,7 @@ test_expect_success !MINGW 'rebase (L/L)' '
        # In this test we want ISO-8859-1 encoded commits as the result
        git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard side &&
        git rebase master &&
@@ -135,7 +135,7 @@ test_expect_success !MINGW 'rebase (L/U)' '
        # to get ISO-8859-1 results.
        git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard side &&
        git rebase master &&
@@ -148,7 +148,7 @@ test_expect_success 'cherry-pick(U/U)' '
 
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -163,7 +163,7 @@ test_expect_success !MINGW 'cherry-pick(L/L)' '
 
        git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -178,7 +178,7 @@ test_expect_success 'cherry-pick(U/L)' '
 
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -194,7 +194,7 @@ test_expect_success !MINGW 'cherry-pick(L/U)' '
 
        git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard master &&
        git cherry-pick side^ &&
@@ -207,7 +207,7 @@ test_expect_success !MINGW 'cherry-pick(L/U)' '
 test_expect_success 'rebase --merge (U/U)' '
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        git reset --hard side &&
        git rebase --merge master &&
@@ -218,7 +218,7 @@ test_expect_success 'rebase --merge (U/U)' '
 test_expect_success 'rebase --merge (U/L)' '
        git config i18n.commitencoding UTF-8 &&
        git config i18n.logoutputencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        git reset --hard side &&
        git rebase --merge master &&
@@ -230,7 +230,7 @@ test_expect_success 'rebase --merge (L/L)' '
        # In this test we want ISO-8859-1 encoded commits as the result
        git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard side &&
        git rebase --merge master &&
@@ -243,7 +243,7 @@ test_expect_success 'rebase --merge (L/U)' '
        # to get ISO-8859-1 results.
        git config i18n.commitencoding ISO8859-1 &&
        git config i18n.logoutputencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard side &&
        git rebase --merge master &&
@@ -254,7 +254,7 @@ test_expect_success 'rebase --merge (L/U)' '
 test_expect_success 'am (U/U)' '
        # Apply UTF-8 patches with UTF-8 commitencoding
        git config i18n.commitencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        git reset --hard master &&
        git am out-u1 out-u2 &&
@@ -265,7 +265,7 @@ test_expect_success 'am (U/U)' '
 test_expect_success !MINGW 'am (L/L)' '
        # Apply ISO-8859-1 patches with ISO-8859-1 commitencoding
        git config i18n.commitencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard master &&
        git am out-l1 out-l2 &&
@@ -276,7 +276,7 @@ test_expect_success !MINGW 'am (L/L)' '
 test_expect_success 'am (U/L)' '
        # Apply ISO-8859-1 patches with UTF-8 commitencoding
        git config i18n.commitencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
        git reset --hard master &&
 
        # am specifies --utf8 by default.
@@ -288,7 +288,7 @@ test_expect_success 'am (U/L)' '
 test_expect_success 'am --no-utf8 (U/L)' '
        # Apply ISO-8859-1 patches with UTF-8 commitencoding
        git config i18n.commitencoding UTF-8 &&
-       . "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       . "$TEST_DIRECTORY"/t3901/utf8.txt &&
 
        git reset --hard master &&
        git am --no-utf8 out-l1 out-l2 2>err &&
@@ -303,7 +303,7 @@ test_expect_success 'am --no-utf8 (U/L)' '
 test_expect_success !MINGW 'am (L/U)' '
        # Apply UTF-8 patches with ISO-8859-1 commitencoding
        git config i18n.commitencoding ISO8859-1 &&
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
 
        git reset --hard master &&
        # mailinfo will re-code the commit message to the charset specified by
diff --git a/t/t3901-utf8.txt b/t/t3901-utf8.txt
deleted file mode 100755 (executable)
index 5f5205c..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-: to be sourced in t3901 -- this is utf8
-GIT_AUTHOR_NAME="Áéí óú" &&
-GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
-export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
diff --git a/t/t3901/8859-1.txt b/t/t3901/8859-1.txt
new file mode 100755 (executable)
index 0000000..38c21a6
--- /dev/null
@@ -0,0 +1,4 @@
+: to be sourced in t3901 -- this is latin-1
+GIT_AUTHOR_NAME="Áéí óú" &&
+GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
+export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
diff --git a/t/t3901/utf8.txt b/t/t3901/utf8.txt
new file mode 100755 (executable)
index 0000000..5f5205c
--- /dev/null
@@ -0,0 +1,4 @@
+: to be sourced in t3901 -- this is utf8
+GIT_AUTHOR_NAME="Áéí óú" &&
+GIT_COMMITTER_NAME=$GIT_AUTHOR_NAME &&
+export GIT_AUTHOR_NAME GIT_COMMITTER_NAME
index b71d1e659e97c0f34bc5bcfeda65b854c2d714e9..3b4bed5c9ace3b9122380287c55b4130135018c7 100755 (executable)
@@ -865,7 +865,7 @@ test_expect_success 'stash push -p with pathspec shows no changes only once' '
        git stash push -p foo >actual &&
        echo "No local changes to save" >expect &&
        git reset --hard HEAD~ &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 test_expect_success 'stash push with pathspec shows no changes when there are none' '
@@ -875,7 +875,7 @@ test_expect_success 'stash push with pathspec shows no changes when there are no
        git stash push foo >actual &&
        echo "No local changes to save" >expect &&
        git reset --hard HEAD~ &&
-       test_cmp expect actual
+       test_i18ncmp expect actual
 '
 
 test_expect_success 'stash push with pathspec not in the repository errors out' '
index 0b4f7dfdc6f6dac4775ab4b016d6be83681ddadd..e2824d343783438f9a3ae78884df7c8b8c63670d 100755 (executable)
@@ -354,7 +354,7 @@ test_expect_failure 'combine diff coalesce three parents' '
 '
 
 # Test for a bug reported at
-# http://thread.gmane.org/gmane.comp.version-control.git/224410
+# https://public-inbox.org/git/20130515143508.GO25742@login.drsnuggles.stderr.nl/
 # where a delete lines were missing from combined diff output when they
 # occurred exactly before the context lines of a later change.
 test_expect_success 'combine diff missing delete bug' '
index 6154acb4569eb452f4e19a40ba40e19703ecca88..3e6b485ecba1a59e3d69edc3ac5a6f0f1d327bd3 100755 (executable)
@@ -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 &&
index d4a3ffa69cd2e88b19e05149d2e752bec89ee57c..33ec26d7557ab49888584396c14a1b58564c2dcd 100755 (executable)
@@ -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
index 556450609b91873efc966d36f20b84f33425b4dc..2affd7a100996f93839eec682e4d7381e3ac0b2a 100755 (executable)
@@ -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 (executable)
index 0000000..bc69e26
--- /dev/null
@@ -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
index f577990716011cfbc9a1b605aa5f23fe0d660c56..66606e7508559e343d1b6959397dec77ddf5c6ac 100755 (executable)
@@ -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
@@ -577,6 +725,18 @@ test_expect_success 'log.decorate configuration' '
 
 '
 
+test_expect_success 'log.decorate config parsing' '
+       git log --oneline --decorate=full >expect.full &&
+       git log --oneline --decorate=short >expect.short &&
+
+       test_config log.decorate full &&
+       test_config log.mailmap true &&
+       git log --oneline >actual &&
+       test_cmp expect.full actual &&
+       git log --oneline --decorate=short >actual &&
+       test_cmp expect.short actual
+'
+
 test_expect_success TTY 'log output on a TTY' '
        git log --oneline --decorate >expect.short &&
 
@@ -1380,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
index 21eb8c8587f2b4de01595d91d27c8ec6706d2a00..18aa1b5889749e706cba70c2a4f2633bcc9c9eb4 100755 (executable)
@@ -126,12 +126,12 @@ test_expect_success 'NUL separation with --stat' '
        test_i18ncmp expected actual
 '
 
-test_expect_failure 'NUL termination with --stat' '
+test_expect_failure C_LOCALE_OUTPUT 'NUL termination with --stat' '
        stat0_part=$(git diff --stat HEAD^ HEAD) &&
        stat1_part=$(git diff-tree --no-commit-id --stat --root HEAD^) &&
        printf "add bar\n$stat0_part\n\0$(commit_msg)\n$stat1_part\n0" >expected &&
        git log -z --stat --pretty="tformat:%s" >actual &&
-       test_i18ncmp expected actual
+       test_cmp expected actual
 '
 
 test_expect_success 'setup more commits' '
index 886b6953e40f9fceae18642ebec031e0bdf511e3..fe2d4f15a73f082c516a03b1877c4cf82982138a 100755 (executable)
@@ -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
index cca23383c584f3f63114f2bb0a4d74aaf45208c7..f6207f42b5a4ed382b819cb81631cd0acab61463 100755 (executable)
@@ -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 (file)
index 0000000..caaf614
Binary files /dev/null and b/t/t5004/big-pack.zip differ
index 43a672c3451146f800852a6d8688c10e497b47cf..9c68b992511b7098df0570ca37e0add468a8a093 100755 (executable)
@@ -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!
 #
index 424bec7d77c2b3761669230bf30030883ce4c230..20e2473a03b645d690b25598bc0cb2e421034b6c 100755 (executable)
@@ -20,11 +20,13 @@ has_any () {
 }
 
 test_expect_success 'setup repo with moderate-sized history' '
-       for i in $(test_seq 1 10); do
+       for i in $(test_seq 1 10)
+       do
                test_commit $i
        done &&
        git checkout -b other HEAD~5 &&
-       for i in $(test_seq 1 10); do
+       for i in $(test_seq 1 10)
+       do
                test_commit side-$i
        done &&
        git checkout master &&
@@ -104,7 +106,8 @@ test_expect_success 'clone from bitmapped repository' '
 '
 
 test_expect_success 'setup further non-bitmapped commits' '
-       for i in $(test_seq 1 10); do
+       for i in $(test_seq 1 10)
+       do
                test_commit further-$i
        done
 '
@@ -289,4 +292,43 @@ test_expect_success 'splitting packs does not generate bogus bitmaps' '
        git -C no-bitmaps.git fetch .. HEAD
 '
 
+test_expect_success 'set up reusable pack' '
+       rm -f .git/objects/pack/*.keep &&
+       git repack -adb &&
+       reusable_pack () {
+               git for-each-ref --format="%(objectname)" |
+               git pack-objects --delta-base-offset --revs --stdout "$@"
+       }
+'
+
+test_expect_success 'pack reuse respects --honor-pack-keep' '
+       test_when_finished "rm -f .git/objects/pack/*.keep" &&
+       for i in .git/objects/pack/*.pack
+       do
+               >${i%.pack}.keep
+       done &&
+       reusable_pack --honor-pack-keep >empty.pack &&
+       git index-pack empty.pack &&
+       >expect &&
+       git show-index <empty.idx >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'pack reuse respects --local' '
+       mv .git/objects/pack/* alt.git/objects/pack/ &&
+       test_when_finished "mv alt.git/objects/pack/* .git/objects/pack/" &&
+       reusable_pack --local >empty.pack &&
+       git index-pack empty.pack &&
+       >expect &&
+       git show-index <empty.idx >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'pack reuse respects --incremental' '
+       reusable_pack --incremental >empty.pack &&
+       git index-pack empty.pack &&
+       >expect &&
+       git show-index <empty.idx >actual &&
+       test_cmp expect actual
+'
 test_done
index 37143ea0ac72814ac539648ee4b47eb895a567c4..2ed479b712aed7a8f11c8495c0eba72788eb4f26 100755 (executable)
@@ -82,12 +82,16 @@ test_expect_success 'packing produces a long delta' '
        # Use --window=0 to make sure we are seeing reused deltas,
        # not computing a new long chain.
        pack=$(git pack-objects --all --window=0 </dev/null pack) &&
-       test 9 = "$(max_chain pack-$pack.pack)"
+       echo 9 >expect &&
+       max_chain pack-$pack.pack >actual &&
+       test_i18ncmp expect actual
 '
 
 test_expect_success '--depth limits depth' '
        pack=$(git pack-objects --all --depth=5 </dev/null pack) &&
-       test 5 = "$(max_chain pack-$pack.pack)"
+       echo 5 >expect &&
+       max_chain pack-$pack.pack >actual &&
+       test_i18ncmp expect actual
 '
 
 test_done
index 3331e0f53443f9a8566d01ac08dd0cbc5aa2d33b..d375d7110d102d6b3ea194e4f09a9b5f391ed496 100755 (executable)
@@ -288,7 +288,10 @@ test_expect_success 'receive-pack de-dupes .have lines' '
        $shared .have
        EOF
 
-       GIT_TRACE_PACKET=$(pwd)/trace git push fork HEAD:foo &&
+       GIT_TRACE_PACKET=$(pwd)/trace \
+           git push \
+               --receive-pack="unset GIT_TRACE_PACKET; git-receive-pack" \
+               fork HEAD:foo &&
        extract_ref_advertisement <trace >refs &&
        test_cmp expect refs
 '
index b5865b385d3a0ca175dc683c75b7f650958a0469..80a1a3239a64a4f551bc394ab88be500c9b5c44f 100755 (executable)
@@ -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
index 17f4d0fe4e7244cb58eaedcf41bcaa9c7b157bb4..f15f7a332960f15c7af32a3c1e7a66f5a8a68989 100755 (executable)
@@ -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 &&
index f3b0a8d30afcb472398bef8623a49ba6bac7dc24..162baf101f340885619e0a1e67b0b14cf37a2347 100755 (executable)
@@ -71,6 +71,16 @@ test_expect_success "fetch --recurse-submodules recurses into submodules" '
        test_i18ncmp expect.err actual.err
 '
 
+test_expect_success "submodule.recurse option triggers recursive fetch" '
+       add_upstream_commit &&
+       (
+               cd downstream &&
+               git -c submodule.recurse fetch >../actual.out 2>../actual.err
+       ) &&
+       test_must_be_empty actual.out &&
+       test_i18ncmp expect.err actual.err
+'
+
 test_expect_success "fetch --recurse-submodules -j2 has the same output behaviour" '
        add_upstream_commit &&
        (
index 57ba3226288cadbbe8af569d13217f5b2c17af90..beff65b8ace505d78c3997a737711ee7fc6383b5 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='unpack-objects'
+test_description='test push with submodules'
 
 . ./test-lib.sh
 
@@ -27,7 +27,7 @@ test_expect_success setup '
        )
 '
 
-test_expect_success push '
+test_expect_success 'push works with recorded gitlink' '
        (
                cd work &&
                git push ../pub.git master
@@ -126,6 +126,27 @@ test_expect_success 'push succeeds if submodule commit not on remote but using o
        )
 '
 
+test_expect_success 'push succeeds if submodule commit not on remote but using auto-on-demand via submodule.recurse config' '
+       (
+               cd work/gar/bage &&
+               >recurse-on-demand-from-submodule-recurse-config &&
+               git add recurse-on-demand-from-submodule-recurse-config &&
+               git commit -m "Recurse submodule.recurse from config junk"
+       ) &&
+       (
+               cd work &&
+               git add gar/bage &&
+               git commit -m "Recurse submodule.recurse from config for gar/bage" &&
+               git -c submodule.recurse push ../pub.git master &&
+               # Check that the supermodule commit got there
+               git fetch ../pub.git &&
+               git diff --quiet FETCH_HEAD master &&
+               # Check that the submodule commit got there too
+               cd gar/bage &&
+               git diff --quiet origin/master master
+       )
+'
+
 test_expect_success 'push recurse-submodules on command line overrides config' '
        (
                cd work/gar/bage &&
index ecb8d446a58d422bd8a9a25f75e4f9a20ec50818..5bcb288f5c4d02e7994d745bc07485bf1bf6c116 100755 (executable)
@@ -124,6 +124,43 @@ test_expect_success GPG 'signed push sends push certificate' '
        test_cmp expect dst/push-cert-status
 '
 
+test_expect_success GPG 'inconsistent push options in signed push not allowed' '
+       # First, invoke receive-pack with dummy input to obtain its preamble.
+       prepare_dst &&
+       git -C dst config receive.certnonceseed sekrit &&
+       git -C dst config receive.advertisepushoptions 1 &&
+       printf xxxx | test_might_fail git receive-pack dst >preamble &&
+
+       # Then, invoke push. Simulate a receive-pack that sends the preamble we
+       # obtained, followed by a dummy packet.
+       write_script myscript <<-\EOF &&
+               cat preamble &&
+               printf xxxx &&
+               cat >push
+       EOF
+       test_might_fail git push --push-option="foo" --push-option="bar" \
+               --receive-pack="\"$(pwd)/myscript\"" --signed dst --delete ff &&
+
+       # Replay the push output on a fresh dst, checking that ff is truly
+       # deleted.
+       prepare_dst &&
+       git -C dst config receive.certnonceseed sekrit &&
+       git -C dst config receive.advertisepushoptions 1 &&
+       git receive-pack dst <push &&
+       test_must_fail git -C dst rev-parse ff &&
+
+       # Tweak the push output to make the push option outside the cert
+       # different, then replay it on a fresh dst, checking that ff is not
+       # deleted.
+       perl -pe "s/([^ ])bar/\$1baz/" push >push.tweak &&
+       prepare_dst &&
+       git -C dst config receive.certnonceseed sekrit &&
+       git -C dst config receive.advertisepushoptions 1 &&
+       git receive-pack dst <push.tweak >out &&
+       git -C dst rev-parse ff &&
+       grep "inconsistent push options" out
+'
+
 test_expect_success GPG 'fail without key and heed user.signingkey' '
        prepare_dst &&
        mkdir -p dst/.git/hooks &&
index f9232f5d0ffff996359a27d5727f645f8fcff6d3..90a4b0d2fe9d8a1d9963bea54d2d1198a24003c2 100755 (executable)
@@ -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
index 87308cdced9af11365d5eb0644b9ac86fac21177..8552184e741fe2465e746a3ac42d19edddb15576 100755 (executable)
@@ -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" &&
index e4850b778c2f20df02ce187cc8cd057df2759d7d..39329eb7a8a64b177794b83ef8828b866fead547 100755 (executable)
@@ -19,6 +19,14 @@ test_expect_success 'clone -c can set multi-keys' '
        test_cmp expect actual
 '
 
+test_expect_success 'clone -c can set multi-keys, including some empty' '
+       rm -rf child &&
+       git clone -c credential.helper= -c credential.helper=hi . child &&
+       printf "%s\n" "" hi >expect &&
+       git --git-dir=child/.git config --get-all credential.helper >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'clone -c without a value is boolean true' '
        rm -rf child &&
        git clone -c core.foo . child &&
index 7ace2535c80e988e353ae09ecbf3bb24d6106ecc..fac5a738519fe4aec0c3b2328c410db693c11f63 100755 (executable)
@@ -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-shallow.sh b/t/t5614-clone-submodules-shallow.sh
new file mode 100755 (executable)
index 0000000..a87d329
--- /dev/null
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+test_description='Test shallow cloning of repos with submodules'
+
+. ./test-lib.sh
+
+pwd=$(pwd)
+
+test_expect_success 'setup' '
+       git checkout -b master &&
+       test_commit commit1 &&
+       test_commit commit2 &&
+       mkdir sub &&
+       (
+               cd sub &&
+               git init &&
+               test_commit subcommit1 &&
+               test_commit subcommit2 &&
+               test_commit subcommit3
+       ) &&
+       git submodule add "file://$pwd/sub" sub &&
+       git commit -m "add submodule"
+'
+
+test_expect_success 'nonshallow clone implies nonshallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules "file://$pwd/." super_clone &&
+       git -C super_clone log --oneline >lines &&
+       test_line_count = 3 lines &&
+       git -C super_clone/sub log --oneline >lines &&
+       test_line_count = 3 lines
+'
+
+test_expect_success 'shallow clone with shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --depth 2 --shallow-submodules "file://$pwd/." super_clone &&
+       git -C super_clone log --oneline >lines &&
+       test_line_count = 2 lines &&
+       git -C super_clone/sub log --oneline >lines &&
+       test_line_count = 1 lines
+'
+
+test_expect_success 'shallow clone does not imply shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
+       git -C super_clone log --oneline >lines &&
+       test_line_count = 2 lines &&
+       git -C super_clone/sub log --oneline >lines &&
+       test_line_count = 3 lines
+'
+
+test_expect_success 'shallow clone with non shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
+       git -C super_clone log --oneline >lines &&
+       test_line_count = 2 lines &&
+       git -C super_clone/sub log --oneline >lines &&
+       test_line_count = 3 lines
+'
+
+test_expect_success 'non shallow clone with shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
+       git -C super_clone log --oneline >lines &&
+       test_line_count = 3 lines &&
+       git -C super_clone/sub log --oneline >lines &&
+       test_line_count = 1 lines
+'
+
+test_expect_success 'clone follows shallow recommendation' '
+       test_when_finished "rm -rf super_clone" &&
+       git config -f .gitmodules submodule.sub.shallow true &&
+       git add .gitmodules &&
+       git commit -m "recommed shallow for sub" &&
+       git clone --recurse-submodules --no-local "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 4 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 1 lines
+       )
+'
+
+test_expect_success 'get unshallow recommended shallow submodule' '
+       test_when_finished "rm -rf super_clone" &&
+       git clone --no-local "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git submodule update --init --no-recommend-shallow &&
+               git log --oneline >lines &&
+               test_line_count = 4 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       )
+'
+
+test_expect_success 'clone follows non shallow recommendation' '
+       test_when_finished "rm -rf super_clone" &&
+       git config -f .gitmodules submodule.sub.shallow false &&
+       git add .gitmodules &&
+       git commit -m "recommed non shallow for sub" &&
+       git clone --recurse-submodules --no-local "file://$pwd/." super_clone &&
+       (
+               cd super_clone &&
+               git log --oneline >lines &&
+               test_line_count = 5 lines
+       ) &&
+       (
+               cd super_clone/sub &&
+               git log --oneline >lines &&
+               test_line_count = 3 lines
+       )
+'
+
+test_done
diff --git a/t/t5614-clone-submodules.sh b/t/t5614-clone-submodules.sh
deleted file mode 100755 (executable)
index a87d329..0000000
+++ /dev/null
@@ -1,122 +0,0 @@
-#!/bin/sh
-
-test_description='Test shallow cloning of repos with submodules'
-
-. ./test-lib.sh
-
-pwd=$(pwd)
-
-test_expect_success 'setup' '
-       git checkout -b master &&
-       test_commit commit1 &&
-       test_commit commit2 &&
-       mkdir sub &&
-       (
-               cd sub &&
-               git init &&
-               test_commit subcommit1 &&
-               test_commit subcommit2 &&
-               test_commit subcommit3
-       ) &&
-       git submodule add "file://$pwd/sub" sub &&
-       git commit -m "add submodule"
-'
-
-test_expect_success 'nonshallow clone implies nonshallow submodule' '
-       test_when_finished "rm -rf super_clone" &&
-       git clone --recurse-submodules "file://$pwd/." super_clone &&
-       git -C super_clone log --oneline >lines &&
-       test_line_count = 3 lines &&
-       git -C super_clone/sub log --oneline >lines &&
-       test_line_count = 3 lines
-'
-
-test_expect_success 'shallow clone with shallow submodule' '
-       test_when_finished "rm -rf super_clone" &&
-       git clone --recurse-submodules --depth 2 --shallow-submodules "file://$pwd/." super_clone &&
-       git -C super_clone log --oneline >lines &&
-       test_line_count = 2 lines &&
-       git -C super_clone/sub log --oneline >lines &&
-       test_line_count = 1 lines
-'
-
-test_expect_success 'shallow clone does not imply shallow submodule' '
-       test_when_finished "rm -rf super_clone" &&
-       git clone --recurse-submodules --depth 2 "file://$pwd/." super_clone &&
-       git -C super_clone log --oneline >lines &&
-       test_line_count = 2 lines &&
-       git -C super_clone/sub log --oneline >lines &&
-       test_line_count = 3 lines
-'
-
-test_expect_success 'shallow clone with non shallow submodule' '
-       test_when_finished "rm -rf super_clone" &&
-       git clone --recurse-submodules --depth 2 --no-shallow-submodules "file://$pwd/." super_clone &&
-       git -C super_clone log --oneline >lines &&
-       test_line_count = 2 lines &&
-       git -C super_clone/sub log --oneline >lines &&
-       test_line_count = 3 lines
-'
-
-test_expect_success 'non shallow clone with shallow submodule' '
-       test_when_finished "rm -rf super_clone" &&
-       git clone --recurse-submodules --no-local --shallow-submodules "file://$pwd/." super_clone &&
-       git -C super_clone log --oneline >lines &&
-       test_line_count = 3 lines &&
-       git -C super_clone/sub log --oneline >lines &&
-       test_line_count = 1 lines
-'
-
-test_expect_success 'clone follows shallow recommendation' '
-       test_when_finished "rm -rf super_clone" &&
-       git config -f .gitmodules submodule.sub.shallow true &&
-       git add .gitmodules &&
-       git commit -m "recommed shallow for sub" &&
-       git clone --recurse-submodules --no-local "file://$pwd/." super_clone &&
-       (
-               cd super_clone &&
-               git log --oneline >lines &&
-               test_line_count = 4 lines
-       ) &&
-       (
-               cd super_clone/sub &&
-               git log --oneline >lines &&
-               test_line_count = 1 lines
-       )
-'
-
-test_expect_success 'get unshallow recommended shallow submodule' '
-       test_when_finished "rm -rf super_clone" &&
-       git clone --no-local "file://$pwd/." super_clone &&
-       (
-               cd super_clone &&
-               git submodule update --init --no-recommend-shallow &&
-               git log --oneline >lines &&
-               test_line_count = 4 lines
-       ) &&
-       (
-               cd super_clone/sub &&
-               git log --oneline >lines &&
-               test_line_count = 3 lines
-       )
-'
-
-test_expect_success 'clone follows non shallow recommendation' '
-       test_when_finished "rm -rf super_clone" &&
-       git config -f .gitmodules submodule.sub.shallow false &&
-       git add .gitmodules &&
-       git commit -m "recommed non shallow for sub" &&
-       git clone --recurse-submodules --no-local "file://$pwd/." super_clone &&
-       (
-               cd super_clone &&
-               git log --oneline >lines &&
-               test_line_count = 5 lines
-       ) &&
-       (
-               cd super_clone/sub &&
-               git log --oneline >lines &&
-               test_line_count = 3 lines
-       )
-'
-
-test_done
index fd401ca605681a1586aa29bd83fe51004bd5c1ab..c670668409817c6d1066de53dc294bade2f27e93 100755 (executable)
@@ -21,16 +21,12 @@ EOF
 test_expect_success 'error message for path inside submodule' '
        echo a >sub/a &&
        test_must_fail git add sub/a 2>actual &&
-       test_cmp expect actual
+       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_cmp expect actual
+       test_i18ngrep "in unpopulated submodule" actual
 '
 
 test_done
index cf076dcd940aa0d74bd8df03b4f6c768e1954455..394b169eada7968875e931c5a43f25a808cfc18f 100755 (executable)
@@ -129,7 +129,7 @@ for repack in '' true; do
        '
 done
 
-test_expect_success 'do not complain about existing broken links' '
+test_expect_success 'do not complain about existing broken links (commit)' '
        cat >broken-commit <<-\EOF &&
        tree 0000000000000000000000000000000000000001
        parent 0000000000000000000000000000000000000002
@@ -144,4 +144,29 @@ test_expect_success 'do not complain about existing broken links' '
        test_must_be_empty stderr
 '
 
+test_expect_success 'do not complain about existing broken links (tree)' '
+       cat >broken-tree <<-\EOF &&
+       100644 blob 0000000000000000000000000000000000000003    foo
+       EOF
+       tree=$(git mktree --missing <broken-tree) &&
+       git gc 2>stderr &&
+       git cat-file -e $tree &&
+       test_must_be_empty stderr
+'
+
+test_expect_success 'do not complain about existing broken links (tag)' '
+       cat >broken-tag <<-\EOF &&
+       object 0000000000000000000000000000000000000004
+       type commit
+       tag broken
+       tagger whatever <whatever@example.com> 1234 -0000
+
+       this is a broken tag
+       EOF
+       tag=$(git hash-object -t tag -w broken-tag) &&
+       git gc 2>stderr &&
+       git cat-file -e $tag &&
+       test_must_be_empty stderr
+'
+
 test_done
index bb2e4d704de006025ae0db9369b81bbebc15750a..0ef7b94394151da9a9a6d602952e7f2fa64cf6a4 100755 (executable)
@@ -87,7 +87,7 @@ test_expect_success 'creating a tag with --create-reflog should create reflog' '
        git tag --create-reflog tag_with_reflog &&
        git reflog exists refs/tags/tag_with_reflog &&
        sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
-       test_cmp expected actual
+       test_i18ncmp expected actual
 '
 
 test_expect_success 'annotated tag with --create-reflog has correct message' '
@@ -98,7 +98,7 @@ test_expect_success 'annotated tag with --create-reflog has correct message' '
        git tag -m "annotated tag" --create-reflog tag_with_reflog &&
        git reflog exists refs/tags/tag_with_reflog &&
        sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
-       test_cmp expected actual
+       test_i18ncmp expected actual
 '
 
 test_expect_success '--create-reflog does not create reflog on failure' '
index 9c9c378119defa7884a21beb27e806ddf1171497..615e7e0162518087fff355bac156ba8bbdd6df38 100755 (executable)
@@ -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
 '
index c27f90f285faf0529f08fae7262a40b605bd7375..a8d9ec498778b5a597fd5751f811c8bf0b0ab926 100755 (executable)
@@ -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 \
index cdc0747bf01b0598a2e864073887896162815019..fc6013ba3c83aa7243f15595bc7b6ddd8b8f46dc 100755 (executable)
@@ -9,6 +9,7 @@ cat >expected <<\EOF
 ?? actual
 ?? expected
 ?? untracked/
+!! untracked/ignored
 EOF
 
 test_expect_success 'status untracked directory with --ignored' '
index 0667bd9dd3e8bc650bc4e47ac9fcde5d63ef0d11..e5fb892f9575fda4baf0b2a0e6b31cf13a0d6c0b 100755 (executable)
@@ -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
index 2eda6adeb14e203d3c3ffe18144d7275d293ce0e..a1cb9ff858e4a2113bbf7fb732b9e6d0cb308445 100755 (executable)
@@ -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_with_args "reset --keep"
+
+test_submodule_forced_switch_recursing_with_args "reset --hard"
+
 test_submodule_switch "git reset --keep"
 
 test_submodule_switch "git reset --merge"
index b89fd2a6ada025fc0e68b1a1c3d7e739aacec688..7b36954d63d70e79149cf1558a93586e1b39c867 100755 (executable)
@@ -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
index 1b8f1dbd3a3d8a59a74888155d36d0d49e94cb64..dcac364c5fa2a8ff7fe264a21fb9682ec934a874 100755 (executable)
@@ -281,7 +281,7 @@ test_expect_success 'submodule add with ./, /.. and // in path' '
        test_cmp empty untracked
 '
 
-test_expect_success 'submodule add with \\ in path' '
+test_expect_success !CYGWIN 'submodule add with \\ in path' '
        test_when_finished "rm -rf parent sub\\with\\backslash" &&
 
        # Initialize a repo with a backslash in its name
index 4ac386d98b8ebdfb4808b9a8119900ee23b5dfa9..034914a14fd49fea07e8bff4802994fcd38807a9 100755 (executable)
@@ -447,7 +447,7 @@ test_expect_success 'submodule update - command run for initial population of su
        EOF
        rm -rf super/submodule &&
        test_must_fail git -C super submodule update 2>actual &&
-       test_cmp expect actual &&
+       test_i18ncmp expect actual &&
        git -C super submodule update --checkout
 '
 
index fb00e6d9b07f4cc6f478bb532749403017599400..79427840a4faed765c06c9c9dcf35e4ec3c43c43 100755 (executable)
@@ -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
@@ -360,7 +392,7 @@ EOF
 test_expect_success 'status -s -b' '
 
        git status -s -b >output &&
-       test_cmp expect output
+       test_i18ncmp expect output
 
 '
 
@@ -370,7 +402,7 @@ test_expect_success 'status -s -z -b' '
        git status -s -z -b >output &&
        nul_to_q <output >output.q &&
        mv output.q output &&
-       test_cmp expect output
+       test_i18ncmp expect output
 '
 
 test_expect_success 'setup dir3' '
@@ -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
@@ -687,7 +744,7 @@ EOF
 test_expect_success 'status -s -b with color.status' '
 
        git status -s -b | test_decode_color >output &&
-       test_cmp expect output
+       test_i18ncmp expect output
 
 '
 
@@ -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)
 
index db9774e345586428ae1ac23f11d92ae00e05c1e9..ddef7ea6b0d87ef5922bf267ee3fdc9ade4383af 100755 (executable)
@@ -101,7 +101,7 @@ test_expect_success '--amend option with empty author' '
        echo "Empty author test" >>foo &&
        test_tick &&
        test_must_fail git commit -a -m "empty author" --amend 2>err &&
-       grep "empty ident" err
+       test_i18ngrep "empty ident" err
 '
 
 test_expect_success '--amend option with missing author' '
@@ -114,7 +114,7 @@ test_expect_success '--amend option with missing author' '
        echo "Missing author test" >>foo &&
        test_tick &&
        test_must_fail git commit -a -m "malformed author" --amend 2>err &&
-       grep "empty ident" err
+       test_i18ngrep "empty ident" err
 '
 
 test_expect_success '--reset-author makes the commit ours even with --amend option' '
index 4dd1d7c52085d25546f64692acd0b351a7dddd72..0c6f91c4338cce131b3d6066e590f177f7e739d1 100755 (executable)
@@ -1258,4 +1258,21 @@ test_expect_success 'with no command and no key' '
        test_cmp expected actual
 '
 
+test_expect_success 'with cut line' '
+       cat >expected <<-\EOF &&
+               my subject
+
+               review: Brian
+               sign: A U Thor <author@example.com>
+               # ------------------------ >8 ------------------------
+               ignore this
+       EOF
+       git interpret-trailers --trailer review:Brian >actual <<-\EOF &&
+               my subject
+               # ------------------------ >8 ------------------------
+               ignore this
+       EOF
+       test_cmp expected actual
+'
+
 test_done
index 7f09867478c408cddee6b60639bfdf7c845421d8..668bbee73c8dc58c6ba61c4a8002ed68206d1445 100755 (executable)
@@ -25,14 +25,14 @@ prompt_given ()
 
 test_expect_success 'basic usage requires no repo' '
        test_expect_code 129 git difftool -h >output &&
-       grep ^usage: output &&
+       test_i18ngrep ^usage: output &&
        # create a ceiling directory to prevent Git from finding a repo
        mkdir -p not/repo &&
        test_when_finished rm -r not &&
        test_expect_code 129 \
        env GIT_CEILING_DIRECTORIES="$(pwd)/not" \
        git -C not/repo difftool -h >output &&
-       grep ^usage: output
+       test_i18ngrep ^usage: output
 '
 
 # Create a file on master and change it on branch
index cee42097b077378f42910666a7895347b0787b1a..f1063878205cfb9b0af8e54d997040ddb65bc27e 100755 (executable)
@@ -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
 '
index 169fd8d7065f89ce78f5241eeb7bf376c3f3172d..0059a1f837882c504e717135c9c92bc5b1d7f62c 100755 (executable)
@@ -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' '
index efef7fb81fa4f8ed56d12c6519cb4ee7500ba3da..701e08a8e5941d711ffbef52da972acb7ab9a41e 100755 (executable)
@@ -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!"
 '
index 5b6eb3a65e26263db526c2e891ae01a4578bf48e..7184113b9b2b381a6e7e0def8a8c1ba06771f8a6 100755 (executable)
@@ -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,36 @@ 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 finds patterns in a submodule via config' '
+       test_config submodule.recurse true &&
+       # expect from previous test
+       git grep -e "(3|4)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'grep --no-recurse-submodules overrides config' '
+       test_config submodule.recurse true &&
+       cat >expect <<-\EOF &&
+       a:(1|2)d(3|4)
+       b/b:(3|4)
+       EOF
+
+       git grep -e "(3|4)" --no-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 +62,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 +72,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 +190,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 +221,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 +304,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 +331,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
index 60a80f60b2685c461f80cdbf4256eb17dc18b85b..d1e4e8ad19f3d3e005e177b98e4eee16f4ef1461 100755 (executable)
@@ -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
index b5149fde6ecda2b3de5ed60f43c60ab70230289c..8dcb05c4a5711e95bb64d8ba310e3490744fc015 100755 (executable)
@@ -70,7 +70,7 @@ test_expect_success 'iso-8859-1' '
 
        git config i18n.commitencoding ISO8859-1 &&
        # use author and committer name in ISO-8859-1 to match it.
-       . "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       . "$TEST_DIRECTORY"/t3901/8859-1.txt &&
        test_tick &&
        echo rosten >file &&
        git commit -s -m den file &&
index 6d06ed96cbc37ee0d19173a4c5379e021c1e5414..cc8d463e01ac74a78fa08b638318a88b8ea98bff 100755 (executable)
@@ -519,7 +519,7 @@ test_expect_success \
 
 test_expect_success \
        'encode(commit): utf8' \
-       '. "$TEST_DIRECTORY"/t3901-utf8.txt &&
+       '. "$TEST_DIRECTORY"/t3901/utf8.txt &&
         test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
         test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
         echo "UTF-8" >> file &&
@@ -529,7 +529,7 @@ test_expect_success \
 
 test_expect_success \
        'encode(commit): iso-8859-1' \
-       '. "$TEST_DIRECTORY"/t3901-8859-1.txt &&
+       '. "$TEST_DIRECTORY"/t3901/8859-1.txt &&
         test_when_finished "GIT_AUTHOR_NAME=\"A U Thor\"" &&
         test_when_finished "GIT_COMMITTER_NAME=\"C O Mitter\"" &&
         echo "ISO-8859-1" >> file &&
index 13b5696822d7cd054ed00cd5b4111ab39d3606cb..4936725c675f9dd3290e40722d8e40d63f3c874c 100644 (file)
@@ -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 243d1fdbbcb1424b0082e1c999920f6a50b32d68..47f60ae151c2cfd9855d1b6f1660ec2ba178bec3 100644 (file)
--- 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 a5721b6731eb89e1db78e41fcf6b8dfd8784841e..fdfcb4a84aa16b8173f8f308f7faecf8e9f000c7 100644 (file)
--- 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);
index 4d33138a7525310af3f9f73b699360f159516ad1..9bfcf870f9078f75349bd3b4976c71bbf5c49ebf 100644 (file)
@@ -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;
        }
index ff776056806dd40ddfb02f566b0cbd4222d0d970..6a42e402b00a3da90a008adf6e4c68dc8c45e516 100644 (file)
@@ -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);
 
@@ -1075,7 +1074,7 @@ static enum interesting do_match(const struct name_entry *entry,
                 * later on.
                 * max_depth is ignored but we may consider support it
                 * in future, see
-                * http://thread.gmane.org/gmane.comp.version-control.git/163757/focus=163840
+                * https://public-inbox.org/git/7vmxo5l2g4.fsf@alter.siamese.dyndns.org/
                 */
                if (ps->recursive && S_ISDIR(entry->mode))
                        return entry_interesting;
diff --git a/tree.c b/tree.c
index ce345c551117215882062f99c931b2182a63931a..603b29ee805b00c4b1eeeebed51c917bfef7c009 100644 (file)
--- 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 d24786cba2ca91d0bffd63490b4f71ac4a58de13..0d4734b94b67603bc8f71a5273c3414f8785e066 100644 (file)
--- 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 *);
index aa15111fefc8ca333edeeeb3ac77a8b946e4c6a5..d38c37e38cf183cf878a9f35b19544d1da0bc438 100644 (file)
@@ -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.
index ffb028d6231e24877b5b6af616d0c3de59516d95..5330c02c1427862be1eea61c8d8e4127909d13fe 100644 (file)
@@ -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 ad6d2910fb58e2f27a52646bd79a0442e689517f..2f87ca69a8299485ee013187f99ffbdb2618d7c8 100644 (file)
--- 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
index 97cba39cdf5b6de75e3df0cd0240cae6adcf3859..5a89db30e3ffcb64ee25597fe014592929e07abb 100644 (file)
@@ -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);
index c8b5adb811c7ba5652b94a6cf6f9ea54d6d9335f..b9a3b71c99fd59312f493d6b1eb24c9a0b44578c 100644 (file)
@@ -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);
index e4b395963b9457a680a369c3e1997c799f8793ab..1846685a21a44decfe5790f0020dcf933c8d6583 100644 (file)
@@ -47,7 +47,7 @@ static struct {
 
 static struct {
        uint32_t revision;
-       unsigned long timestamp;
+       timestamp_t timestamp;
        struct strbuf log, author, note;
 } rev_ctx;
 
index 2c86e406f92d9bf0063e6be4f9d76fdbbac17c89..274f1a4935798cba3e8cc7250559f14305678419 100644 (file)
--- 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;
index bae787cf8d7e968e8d5118bf54f17112c0229bf5..2801c6d52bdaa1fb83a0069c8d2217ea129fd6bf 100644 (file)
@@ -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;
 }
 
@@ -399,6 +351,7 @@ int submodule_uses_worktrees(const char *path)
 
        /* The env would be set for the superproject. */
        get_common_dir_noenv(&sb, submodule_gitdir);
+       free(submodule_gitdir);
 
        /*
         * The check below is only known to be good for repository format
@@ -418,7 +371,6 @@ int submodule_uses_worktrees(const char *path)
        /* See if there is any file inside the worktrees directory. */
        dir = opendir(sb.buf);
        strbuf_release(&sb);
-       free(submodule_gitdir);
 
        if (!dir)
                return 0;
index 6bfb985203070e3051f82348568840f5577bf0dc..5ea5e503fbe491a76d3c8ce279b7e281a5d07d3e 100644 (file)
@@ -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;
index d83741770949f457b364896b6ff8632c0a700d69..708e98a96585f8f9a800e9ddae942b94ea7372b8 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -583,8 +583,8 @@ void warn_on_inaccessible(const char *path)
 
 static int access_error_is_ok(int err, unsigned flag)
 {
-       return err == ENOENT || err == ENOTDIR ||
-               ((flag & ACCESS_EACCES_OK) && err == EACCES);
+       return (is_missing_file_error(err) ||
+               ((flag & ACCESS_EACCES_OK) && err == EACCES));
 }
 
 int access_or_warn(const char *path, int mode, unsigned flag)
index 03754849626d1bf117e3821fb7cdffcd594f694f..25aafc35c8332a311ce89002e7c17b200ccbfde6 100644 (file)
@@ -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];
@@ -896,17 +896,18 @@ static void wt_longstatus_print_other(struct wt_status *s,
        status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
 }
 
-void wt_status_truncate_message_at_cut_line(struct strbuf *buf)
+size_t wt_status_locate_end(const char *s, size_t len)
 {
        const char *p;
        struct strbuf pattern = STRBUF_INIT;
 
        strbuf_addf(&pattern, "\n%c %s", comment_line_char, cut_line);
-       if (starts_with(buf->buf, pattern.buf + 1))
-               strbuf_setlen(buf, 0);
-       else if ((p = strstr(buf->buf, pattern.buf)))
-               strbuf_setlen(buf, p - buf->buf + 1);
+       if (starts_with(s, pattern.buf + 1))
+               len = 0;
+       else if ((p = strstr(s, pattern.buf)))
+               len = p - s + 1;
        strbuf_release(&pattern);
+       return len;
 }
 
 void wt_status_add_cut_line(FILE *fp)
@@ -1002,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)
@@ -1082,29 +1083,29 @@ static char *read_line_from_git_path(const char *filename)
 static int split_commit_in_progress(struct wt_status *s)
 {
        int split_in_progress = 0;
-       char *head = read_line_from_git_path("HEAD");
-       char *orig_head = read_line_from_git_path("ORIG_HEAD");
-       char *rebase_amend = read_line_from_git_path("rebase-merge/amend");
-       char *rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
+       char *head, *orig_head, *rebase_amend, *rebase_orig_head;
 
-       if (!head || !orig_head || !rebase_amend || !rebase_orig_head ||
+       if ((!s->amend && !s->nowarn && !s->workdir_dirty) ||
            !s->branch || strcmp(s->branch, "HEAD"))
-               return split_in_progress;
+               return 0;
 
-       if (!strcmp(rebase_amend, rebase_orig_head)) {
-               if (strcmp(head, rebase_amend))
-                       split_in_progress = 1;
-       } else if (strcmp(orig_head, rebase_orig_head)) {
-               split_in_progress = 1;
-       }
+       head = read_line_from_git_path("HEAD");
+       orig_head = read_line_from_git_path("ORIG_HEAD");
+       rebase_amend = read_line_from_git_path("rebase-merge/amend");
+       rebase_orig_head = read_line_from_git_path("rebase-merge/orig-head");
 
-       if (!s->amend && !s->nowarn && !s->workdir_dirty)
-               split_in_progress = 0;
+       if (!head || !orig_head || !rebase_amend || !rebase_orig_head)
+               ; /* fall through, no split in progress */
+       else if (!strcmp(rebase_amend, rebase_orig_head))
+               split_in_progress = !!strcmp(head, rebase_amend);
+       else if (strcmp(orig_head, rebase_orig_head))
+               split_in_progress = 1;
 
        free(head);
        free(orig_head);
        free(rebase_amend);
        free(rebase_orig_head);
+
        return split_in_progress;
 }
 
@@ -1168,6 +1169,7 @@ static int read_rebase_todolist(const char *fname, struct string_list *lines)
                abbrev_sha1_in_line(&line);
                string_list_append(lines, line.buf);
        }
+       fclose(f);
        return 0;
 }
 
@@ -1387,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;
@@ -1428,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))
index 6018c627b1e40e5c36d0c0c12eff1f262cb439b4..8a3864783b039d92454cfda1c2ef2457e737cddd 100644 (file)
@@ -112,7 +112,7 @@ struct wt_status_state {
        unsigned char cherry_pick_head_sha1[20];
 };
 
-void wt_status_truncate_message_at_cut_line(struct strbuf *);
+size_t wt_status_locate_end(const char *s, size_t len);
 void wt_status_add_cut_line(FILE *fp);
 void wt_status_prepare(struct wt_status *s);
 void wt_status_print(struct wt_status *s);