Merge branch 'ps/pathspec-empty-prefix-origin'
authorJunio C Hamano <gitster@pobox.com>
Wed, 26 Apr 2017 06:39:03 +0000 (15:39 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 26 Apr 2017 06:39:03 +0000 (15:39 +0900)
A recent update broke "git add -p ../foo" from a subdirectory.

* ps/pathspec-empty-prefix-origin:
pathspec: honor `PATHSPEC_PREFIX_ORIGIN` with empty prefix

250 files changed:
.mailmap
.travis.yml
Documentation/RelNotes/2.12.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.12.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.13.0.txt
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/git-bisect.txt
Documentation/git-branch.txt
Documentation/git-checkout.txt
Documentation/git-clone.txt
Documentation/git-commit.txt
Documentation/git-credential-cache.txt
Documentation/git-describe.txt
Documentation/git-diff.txt
Documentation/git-for-each-ref.txt
Documentation/git-format-patch.txt
Documentation/git-ls-files.txt
Documentation/git-merge.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-receive-pack.txt
Documentation/git-send-email.txt
Documentation/git-send-pack.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-tag.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/revisions.txt
Documentation/technical/api-hashmap.txt
Documentation/technical/api-oid-array.txt [new file with mode: 0644]
Documentation/technical/api-sha1-array.txt [deleted file]
Documentation/technical/pack-protocol.txt
GIT-VERSION-GEN
Makefile
abspath.c
apply.c
bisect.c
branch.c
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/bundle.c
builtin/cat-file.c
builtin/checkout.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/describe.c
builtin/diff.c
builtin/difftool.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/for-each-ref.c
builtin/fsck.c
builtin/gc.c
builtin/grep.c
builtin/hash-object.c
builtin/index-pack.c
builtin/log.c
builtin/ls-files.c
builtin/ls-remote.c
builtin/mailinfo.c
builtin/merge-file.c
builtin/merge-index.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/pack-refs.c
builtin/patch-id.c
builtin/pull.c
builtin/push.c
builtin/read-tree.c
builtin/receive-pack.c
builtin/replace.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/send-pack.c
builtin/shortlog.c
builtin/submodule--helper.c
builtin/tag.c
builtin/update-index.c
builtin/worktree.c
bulk-checkin.c
cache.h
ci/run-windows-build.sh [new file with mode: 0755]
combine-diff.c
commit.h
config.c
connect.c
contrib/completion/git-completion.bash
contrib/completion/git-completion.zsh
contrib/completion/git-prompt.sh
contrib/git-resurrect.sh
credential-cache.c
credential-store.c
daemon.c
diff-no-index.c
diff.c
diff.h
diffcore-pickaxe.c
dir.c
entry.c
environment.c
fast-import.c
fetch-pack.c
fetch-pack.h
fsck.c
fsck.h
git-add--interactive.perl
git-compat-util.h
git-p4.py
git-rebase.sh
git-stash.sh
git-submodule.sh
git.c
grep.c
hash.h [new file with mode: 0644]
hashmap.c
hashmap.h
hex.c
http-push.c
http.c
http.h
ident.c
imap-send.c
mailinfo.c
name-hash.c
notes.c
pack-bitmap-write.c
pack-write.c
pack.h
pager.c
parse-options-cb.c
parse-options.c
parse-options.h
patch-ids.c
patch-ids.h
path.c
pathspec.c
po/de.po
read-cache.c
ref-filter.c
ref-filter.h
refs.c
refs.h
refs/files-backend.c
refs/refs-internal.h
remote-curl.c
remote.c
remote.h
revision.h
run-command.c
send-pack.c
send-pack.h
sequencer.c
server-info.c
setup.c
sha1-array.c
sha1-array.h
sha1_file.c
sha1_name.c
sha1dc/LICENSE.txt [new file with mode: 0644]
sha1dc/sha1.c [new file with mode: 0644]
sha1dc/sha1.h [new file with mode: 0644]
sha1dc/ubc_check.c [new file with mode: 0644]
sha1dc/ubc_check.h [new file with mode: 0644]
shallow.c
strbuf.c
string-list.c
submodule-config.c
submodule-config.h
submodule.c
submodule.h
t/README
t/helper/.gitignore
t/helper/test-config.c
t/helper/test-lazy-init-name-hash.c [new file with mode: 0644]
t/helper/test-online-cpus.c [new file with mode: 0644]
t/helper/test-read-cache.c
t/helper/test-ref-store.c [new file with mode: 0644]
t/helper/test-sha1-array.c
t/lib-submodule-update.sh
t/perf/p0004-lazy-init-name-hash.sh [new file with mode: 0755]
t/perf/p0005-status.sh [new file with mode: 0755]
t/t0001-init.sh
t/t0013-sha1dc.sh [new file with mode: 0755]
t/t0013/shattered-1.pdf [new file with mode: 0644]
t/t0025-crlf-auto.sh
t/t0301-credential-cache.sh
t/t1007-hash-object.sh
t/t1013-read-tree-submodule.sh
t/t1060-object-corruption.sh
t/t1305-config-include.sh
t/t1309-early-config.sh [new file with mode: 0755]
t/t1400-update-ref.sh
t/t1405-main-ref-store.sh [new file with mode: 0755]
t/t1406-submodule-ref-store.sh [new file with mode: 0755]
t/t1450-fsck.sh
t/t1507-rev-parse-upstream.sh
t/t1514-rev-parse-push.sh
t/t2013-checkout-submodule.sh
t/t2027-worktree-list.sh
t/t3007-ls-files-recurse-submodules.sh
t/t3008-ls-files-lazy-init-name-hash.sh [new file with mode: 0755]
t/t3200-branch.sh
t/t3201-branch-contains.sh
t/t3428-rebase-signoff.sh [new file with mode: 0755]
t/t3502-cherry-pick-merge.sh
t/t3600-rm.sh
t/t3903-stash.sh
t/t3904-stash-patch.sh
t/t4060-diff-submodule-option-diff-format.sh
t/t4062-diff-pickaxe.sh
t/t4150-am.sh
t/t4202-log.sh
t/t5531-deep-submodule-push.sh
t/t5545-push-options.sh
t/t5547-push-quarantine.sh
t/t5601-clone.sh
t/t5615-alternate-env.sh
t/t6120-describe.sh
t/t6302-for-each-ref-filter.sh
t/t6500-gc.sh
t/t7004-tag.sh
t/t7006-pager.sh
t/t7030-verify-tag.sh
t/t7400-submodule-basic.sh
t/t7406-submodule-update.sh
t/t7413-submodule-is-active.sh [new file with mode: 0755]
t/t7504-commit-msg-hook.sh
t/t7506-status-submodule.sh
t/t7800-difftool.sh
t/t7814-grep-recurse-submodules.sh
t/t9807-git-p4-submit.sh
t/t9902-completion.sh
t/test-lib-functions.sh
t/test-lib.sh
transport-helper.c
transport.c
unpack-trees.c
unpack-trees.h
worktree.c
wrapper.c
wt-status.c
wt-status.h
index e06526a49300eb78e5cac3504902d6a3b1053324..ab85e0d16d6383b13954220a0b41202bd68d5d73 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -151,7 +151,8 @@ Matthias Kestenholz <matthias@spinlock.ch> <mk@spinlock.ch>
 Matthias Urlichs <matthias@urlichs.de> <smurf@kiste.(none)>
 Matthias Urlichs <matthias@urlichs.de> <smurf@smurf.noris.de>
 Michael Coleman <tutufan@gmail.com>
-Michael J Gruber <git@drmicha.warpmail.net> <michaeljgruber+gmane@fastmail.fm>
+Michael J Gruber <git@grubix.eu> <michaeljgruber+gmane@fastmail.fm>
+Michael J Gruber <git@grubix.eu> <git@drmicha.warpmail.net>
 Michael S. Tsirkin <mst@kernel.org> <mst@redhat.com>
 Michael S. Tsirkin <mst@kernel.org> <mst@mellanox.co.il>
 Michael S. Tsirkin <mst@kernel.org> <mst@dev.mellanox.co.il>
index 591cc57b80aa415be9c20b3c0dc746d792e68071..c757a111ce3ec59f503274fd13d9191c0c70a0fa 100644 (file)
@@ -39,6 +39,17 @@ env:
 
 matrix:
   include:
+    - env: Windows
+      os: linux
+      compiler:
+      addons:
+      before_install:
+      before_script:
+      script:
+        - >
+          test "$TRAVIS_REPO_SLUG" != "git/git" ||
+          ci/run-windows-build.sh $TRAVIS_BRANCH $(git rev-parse HEAD)
+      after_failure:
     - env: Linux32
       os: linux
       services:
diff --git a/Documentation/RelNotes/2.12.2.txt b/Documentation/RelNotes/2.12.2.txt
new file mode 100644 (file)
index 0000000..4419397
--- /dev/null
@@ -0,0 +1,83 @@
+Git v2.12.2 Release Notes
+=========================
+
+Fixes since v2.12.1
+-------------------
+
+ * "git status --porcelain" is supposed to give a stable output, but a
+   few strings were left as translatable by mistake.
+
+ * "Dumb http" transport used to misparse a nonsense http-alternates
+   response, which has been fixed.
+
+ * "git diff --quiet" relies on the size field in diff_filespec to be
+   correctly populated, but diff_populate_filespec() helper function
+   made an incorrect short-cut when asked only to populate the size
+   field for paths that need to go through convert_to_git() (e.g. CRLF
+   conversion).
+
+ * There is no need for Python only to give a few messages to the
+   standard error stream, but we somehow did.
+
+ * A leak in a codepath to read from a packed object in (rare) cases
+   has been plugged.
+
+ * "git upload-pack", which is a counter-part of "git fetch", did not
+   report a request for a ref that was not advertised as invalid.
+   This is generally not a problem (because "git fetch" will stop
+   before making such a request), but is the right thing to do.
+
+ * A "gc.log" file left by a backgrounded "gc --auto" disables further
+   automatic gc; it has been taught to run at least once a day (by
+   default) by ignoring a stale "gc.log" file that is too old.
+
+ * "git remote rm X", when a branch has remote X configured as the
+   value of its branch.*.remote, tried to remove branch.*.remote and
+   branch.*.merge and failed if either is unset.
+
+ * A caller of tempfile API that uses stdio interface to write to
+   files may ignore errors while writing, which is detected when
+   tempfile is closed (with a call to ferror()).  By that time, the
+   original errno that may have told us what went wrong is likely to
+   be long gone and was overwritten by an irrelevant value.
+   close_tempfile() now resets errno to EIO to make errno at least
+   predictable.
+
+ * "git show-branch" expected there were only very short branch names
+   in the repository and used a fixed-length buffer to hold them
+   without checking for overflow.
+
+ * The code that parses header fields in the commit object has been
+   updated for (micro)performance and code hygiene.
+
+ * A test that creates a confusing branch whose name is HEAD has been
+   corrected not to do so.
+
+ * "Cc:" on the trailer part does not have to conform to RFC strictly,
+   unlike in the e-mail header.  "git send-email" has been updated to
+   ignore anything after '>' when picking addresses, to allow non-address
+   cruft like " # stable 4.4" after the address.
+
+ * "git push" had a handful of codepaths that could lead to a deadlock
+   when unexpected error happened, which has been fixed.
+
+ * Code to read submodule.<name>.ignore config did not state the
+   variable name correctly when giving an error message diagnosing
+   misconfiguration.
+
+ * "git ls-remote" and "git archive --remote" are designed to work
+   without being in a directory under Git's control.  However, recent
+   updates revealed that we randomly look into a directory called
+   .git/ without actually doing necessary set-up when working in a
+   repository.  Stop doing so.
+
+ * The code to parse the command line "git grep <patterns>... <rev>
+   [[--] <pathspec>...]" has been cleaned up, and a handful of bugs
+   have been fixed (e.g. we used to check "--" if it is a rev).
+
+ * The code to parse "git -c VAR=VAL cmd" and set configuration
+   variable for the duration of cmd had two small bugs, which have
+   been fixed.
+   This supersedes jc/config-case-cmdline topic that has been discarded.
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.12.3.txt b/Documentation/RelNotes/2.12.3.txt
new file mode 100644 (file)
index 0000000..73ce7da
--- /dev/null
@@ -0,0 +1,57 @@
+Git v2.12.3 Release Notes
+=========================
+
+Fixes since v2.12.2
+-------------------
+
+ * The "parse_config_key()" API function has been cleaned up.
+
+ * An helper function to make it easier to append the result from
+   real_path() to a strbuf has been added.
+
+ * The t/perf performance test suite was not prepared to test not so
+   old versions of Git, but now it covers versions of Git that are not
+   so ancient.
+
+ * Picking two versions of Git and running tests to make sure the
+   older one and the newer one interoperate happily has now become
+   possible.
+
+ * Teach the "debug" helper used in the test framework that allows a
+   command to run under "gdb" to make the session interactive.
+
+ * "git repack --depth=<n>" for a long time busted the specified depth
+   when reusing delta from existing packs.  This has been corrected.
+
+ * user.email that consists of only cruft chars should consistently
+   error out, but didn't.
+
+ * A few tests were run conditionally under (rare) conditions where
+   they cannot be run (like running cvs tests under 'root' account).
+
+ * "git branch @" created refs/heads/@ as a branch, and in general the
+   code that handled @{-1} and @{upstream} was a bit too loose in
+   disambiguating.
+
+ * "git fetch" that requests a commit by object name, when the other
+   side does not allow such an request, failed without much
+   explanation.
+
+ * "git filter-branch --prune-empty" drops a single-parent commit that
+   becomes a no-op, but did not drop a root commit whose tree is empty.
+
+ * Recent versions of Git treats http alternates (used in dumb http
+   transport) just like HTTP redirects and requires the client to
+   enable following it, due to security concerns.  But we forgot to
+   give a warning when we decide not to honor the alternates.
+
+ * NO_PTHREADS build has been broken for some time; now fixed.
+
+ * Fix for potential segv introduced in v2.11.0 and later (also
+   v2.10.2).
+
+ * A few unterminated here documents in tests were fixed, which in
+   turn revealed incorrect expectations the tests make. These tests
+   have been updated.
+
+Also contains various documentation updates and code clean-ups.
index 5c2348db6cf66551d3e9bc009304b14b7742b275..b757fd119c04a6d9b2fc6414ea3e39042ea4e033 100644 (file)
@@ -12,8 +12,18 @@ Backward compatibility notes.
    release (yet).
 
  * The historical argument order "git merge <msg> HEAD <commit>..."
-   has been deprecated for quite some time, and will be removed in a
-   future release.
+   has been deprecated for quite some time, and is now removed.
+
+ * The default location "~/.git-credential-cache/socket" for the
+   socket used to communicate with the credential-cache daemon has
+   been moved to "~/.cache/git/credential/socket".
+
+ * 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.
 
 
 Updates since v2.12
@@ -77,19 +87,105 @@ UI, Workflows & Features
    unlike in the e-mail header.  "git send-email" has been updated to
    ignore anything after '>' when picking addresses, to allow non-address
    cruft like " # stable 4.4" after the address.
-   (merge 9d3343961b jh/send-email-one-cc later to maint).
 
  * When "git submodule init" decides that the submodule in the working
    tree is its upstream, it now gives a warning as it is not a very
    common setup.
-   (merge d1b3b81aab sb/submodule-init-url-selection later to maint).
 
- * "git stash save" takes a pathspec so that the local changes can be
+ * "git stash push" takes a pathspec so that the local changes can be
    stashed away only partially.
-   (merge 9e140909f6 tg/stash-push later to maint).
 
  * Documentation for "git ls-files" did not refer to core.quotePath.
 
+ * The experimental "split index" feature has gained a few
+   configuration variables to make it easier to use.
+
+ * From a working tree of a repository, a new option of "rev-parse"
+   lets you ask if the repository is used as a submodule of another
+   project, and where the root level of the working tree of that
+   project (i.e. your superproject) is.
+
+ * The pathspec mechanism learned to further limit the paths that
+   match the pattern to those that have specified attributes attached
+   via the gitattributes mechanism.
+
+ * Our source code has used the SHA1_HEADER cpp macro after "#include"
+   in the C code to switch among the SHA-1 implementations. Instead,
+   list the exact header file names and switch among implementations
+   using "#ifdef BLK_SHA1/#include "block-sha1/sha1.h"/.../#endif";
+   this helps some IDE tools.
+
+ * The start-up sequence of "git" needs to figure out some configured
+   settings before it finds and set itself up in the location of the
+   repository and was quite messy due to its "chicken-and-egg" nature.
+   The code has been restructured.
+
+ * The command line prompt (in contrib/) learned a new 'tag' style
+   that can be specified with GIT_PS1_DESCRIBE_STYLE, to describe a
+   detached HEAD with "git describe --tags".
+
+ * The configuration file learned a new "includeIf.<condition>.path"
+   that includes the contents of the given path only when the
+   condition holds.  This allows you to say "include this work-related
+   bit only in the repositories under my ~/work/ directory".
+
+ * Recent update to "rebase -i" started showing a message that is not
+   a warning with "warning:" prefix by mistake.  This has been fixed.
+
+ * Recently we started passing the "--push-options" through the
+   external remote helper interface; now the "smart HTTP" remote
+   helper understands what to do with the passed information.
+
+ * "git describe --dirty" dies when it cannot be determined if the
+   state in the working tree matches that of HEAD (e.g. broken
+   repository or broken submodule).  The command learned a new option
+   "git describe --broken" to give "$name-broken" (where $name is the
+   description of HEAD) in such a case.
+
+ * "git checkout" is taught the "--recurse-submodules" option.
+
+ * Recent enhancement to "git stash push" command to support pathspec
+   to allow only a subset of working tree changes to be stashed away
+   was found to be too chatty and exposed the internal implementation
+   detail (e.g. when it uses reset to match the index to HEAD before
+   doing other things, output from reset seeped out).  These, and
+   other chattyness has been fixed.
+
+ * "git merge <message> HEAD <commit>" syntax that has been deprecated
+   since October 2007 has been removed.
+
+ * The refs completion for large number of refs has been sped up,
+   partly by giving up disambiguating ambiguous refs and partly by
+   eliminating most of the shell processing between 'git for-each-ref'
+   and 'ls-remote' and Bash's completion facility.
+
+ * On many keyboards, typing "@{" involves holding down SHIFT key and
+   one can easily end up with "@{Up..." when typing "@{upstream}".  As
+   the upstream/push keywords do not appear anywhere else in the syntax,
+   we can safely accept them case insensitively without introducing
+   ambiguity or confusion to solve this.
+
+ * "git tag/branch/for-each-ref" family of commands long allowed to
+   filter the refs by "--contains X" (show only the refs that are
+   descendants of X), "--merged X" (show only the refs that are
+   ancestors of X), "--no-merged X" (show only the refs that are not
+   ancestors of X).  One curious omission, "--no-contains X" (show
+   only the refs that are not descendants of X) has been added to
+   them.
+
+ * The default behaviour of "git log" in an interactive session has
+   been changed to enable "--decorate".
+
+ * The output from "git status --short" has been extended to show
+   various kinds of dirtyness in submodules differently; instead of to
+   "M" for modified, 'm' and '?' can be shown to signal changes only
+   to the working tree of the submodule but not the commit that is
+   checked out.
+
+ * Allow the http.postbuffer configuration variable to be set to a
+   size that can be expressed in size_t, which can be larger than
+   ulong on some platforms.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -114,19 +210,15 @@ Performance, Internal Implementation, Development Support etc.
    errno from failed system calls.
 
  * The "parse_config_key()" API function has been cleaned up.
-   (merge ad8c7cdadd jk/parse-config-key-cleanup later to maint).
 
  * A test that creates a confusing branch whose name is HEAD has been
    corrected not to do so.
-   (merge f0252ca23c jk/t6300-cleanup later to maint).
 
  * The code that parses header fields in the commit object has been
    updated for (micro)performance and code hygiene.
-   (merge b072504ce1 rs/commit-parsing-optim later to maint).
 
  * An helper function to make it easier to append the result from
    real_path() to a strbuf has been added.
-   (merge 33ad9ddd0b rs/strbuf-add-real-path later to maint).
 
  * Reduce authentication round-trip over HTTP when the server supports
    just a single authentication method.  This also improves the
@@ -134,26 +226,81 @@ Performance, Internal Implementation, Development Support etc.
    against a server that does not authenticate without a username
    (i.e. not using Kerberos etc., which makes http.emptyAuth
    pointless).
-   (merge 40a18fc77c jk/http-auth later to maint).
 
  * Windows port wants to use OpenSSL's implementation of SHA-1
    routines, so let them.
-   (merge 2cfc70f0de jh/mingw-openssl-sha1 later to maint).
 
  * The t/perf performance test suite was not prepared to test not so
    old versions of Git, but now it covers versions of Git that are not
    so ancient.
-   (merge 28e1fb5466 jt/perf-updates later to maint).
 
  * Add 32-bit Linux variant to the set of platforms to be tested with
    Travis CI.
-   (merge 88dedd5e72 js/travis-32bit-linux later to maint).
 
  * "git branch --list" takes the "--abbrev" and "--no-abbrev" options
    to control the output of the object name in its "-v"(erbose)
    output, but a recent update started ignoring them; fix it before
    the breakage reaches to any released version.
 
+ * Picking two versions of Git and running tests to make sure the
+   older one and the newer one interoperate happily has now become
+   possible.
+
+ * "git tag --contains" used to (ab)use the object bits to keep track
+   of the state of object reachability without clearing them after
+   use; this has been cleaned up and made to use the newer commit-slab
+   facility.
+
+ * The "debug" helper used in the test framework learned to run
+   a command under "gdb" interactively.
+
+ * The "detect attempt to create collisions" variant of SHA-1
+   implementation by Marc Stevens (CWI) and Dan Shumow (Microsoft)
+   has been integrated and made the default.
+
+ * The test framework learned to detect unterminated here documents.
+
+ * The name-hash used for detecting paths that are different only in
+   cases (which matter on case insensitive filesystems) has been
+   optimized to take advantage of multi-threading when it makes sense.
+
+ * An earlier version of sha1dc/sha1.c that was merged to 'master'
+   compiled incorrectly on Windows, which has been fixed.
+
+ * "what URL do we want to update this submodule?" and "are we
+   interested in this submodule?" are split into two distinct
+   concepts, and then the way used to express the latter got extended,
+   paving a way to make it easier to manage a project with many
+   submodules and make it possible to later extend use of multiple
+   worktrees for a project with submodules.
+
+ * Some debugging output from "git describe" were marked for l10n,
+   but some weren't.  Mark missing ones for l10n.
+
+ * Define a new task in .travis.yml that triggers a test session on
+   Windows run elsewhere.
+
+ * Conversion from uchar[20] to struct object_id continues.
+
+ * The "submodule" specific field in the ref_store structure is
+   replaced with a more generic "gitdir" that can later be used also
+   when dealing with ref_store that represents the set of refs visible
+   from the other worktrees.
+
+ * The string-list API used a custom reallocation strategy that was
+   very inefficient, instead of using the usual ALLOC_GROW() macro,
+   which has been fixed.
+   (merge 950a234cbd jh/string-list-micro-optim later to maint).
+
+ * In a 2- and 3-way merge of trees, more than one source trees often
+   end up sharing an identical subtree; optimize by not reading the
+   same tree multiple times in such a case.
+   (merge d12a8cf0af jh/unpack-trees-micro-optim later to maint).
+
+ * The index file has a trailing SHA-1 checksum to detect file
+   corruption, and historically we checked it every time the index
+   file is used.  Omit the validation during normal use, and instead
+   verify only in "git fsck".
 
 Also contains various documentation updates and code clean-ups.
 
@@ -161,30 +308,26 @@ Also contains various documentation updates and code clean-ups.
 Fixes since v2.12
 -----------------
 
-Unless otherwise noted, all the fixes since v2.9 in the maintenance
+Unless otherwise noted, all the fixes since v2.12 in the maintenance
 track are contained in this release (see the maintenance releases'
 notes for details).
 
  * "git repack --depth=<n>" for a long time busted the specified depth
    when reusing delta from existing packs.  This has been corrected.
-   (merge 42b766d765 jk/delta-chain-limit later to maint).
 
  * The code to parse the command line "git grep <patterns>... <rev>
    [[--] <pathspec>...]" has been cleaned up, and a handful of bugs
    have been fixed (e.g. we used to check "--" if it is a rev).
-   (merge 131f3c96d2 jk/grep-no-index-fix later to maint).
 
  * "git ls-remote" and "git archive --remote" are designed to work
    without being in a directory under Git's control.  However, recent
    updates revealed that we randomly look into a directory called
    .git/ without actually doing necessary set-up when working in a
    repository.  Stop doing so.
-   (merge 4b0c3c7735 jn/remote-helpers-with-git-dir later to maint).
 
  * "git show-branch" expected there were only very short branch names
    in the repository and used a fixed-length buffer to hold them
    without checking for overflow.
-   (merge d3cc5f4c44 jk/show-branch-lift-name-len-limit later to maint).
 
  * A caller of tempfile API that uses stdio interface to write to
    files may ignore errors while writing, which is detected when
@@ -193,105 +336,224 @@ notes for details).
    be long gone and was overwritten by an irrelevant value.
    close_tempfile() now resets errno to EIO to make errno at least
    predictable.
-   (merge 7e8c9355b7 jk/tempfile-ferror-fclose-confusion later to maint).
 
  * "git remote rm X", when a branch has remote X configured as the
    value of its branch.*.remote, tried to remove branch.*.remote and
    branch.*.merge and failed if either is unset.
-   (merge 20690b2139 rl/remote-allow-missing-branch-name-merge later to maint).
 
  * A "gc.log" file left by a backgrounded "gc --auto" disables further
    automatic gc; it has been taught to run at least once a day (by
    default) by ignoring a stale "gc.log" file that is too old.
-   (merge a831c06a2b dt/gc-ignore-old-gc-logs later to maint).
 
  * The code to parse "git -c VAR=VAL cmd" and set configuration
    variable for the duration of cmd had two small bugs, which have
    been fixed.
-   (merge 1274a155af jc/config-case-cmdline-take-2 later to maint).
 
  * user.email that consists of only cruft chars should consistently
    error out, but didn't.
-   (merge 94425552f3 jk/ident-empty later to maint).
 
  * "git upload-pack", which is a counter-part of "git fetch", did not
    report a request for a ref that was not advertised as invalid.
    This is generally not a problem (because "git fetch" will stop
    before making such a request), but is the right thing to do.
-   (merge bdb31eada7 jt/upload-pack-error-report later to maint).
 
  * A leak in a codepath to read from a packed object in (rare) cases
    has been plugged.
-   (merge 886ddf4777 rs/sha1-file-plug-fallback-base-leak later to maint).
 
  * When a redirected http transport gets an error during the
    redirected request, we ignored the error we got from the server,
    and ended up giving a not-so-useful error message.
-   (merge 8e27391a5f jt/http-base-url-update-upon-redirect later to maint).
 
  * The patch subcommand of "git add -i" was meant to have paths
    selection prompt just like other subcommand, unlike "git add -p"
    directly jumps to hunk selection.  Recently, this was broken and
    "add -i" lost the paths selection dialog, but it now has been
    fixed.
-   (merge c852bd54bd jk/add-i-patch-do-prompt later to maint).
 
  * Git v2.12 was shipped with an embarrassing breakage where various
    operations that verify paths given from the user stopped dying when
    seeing an issue, and instead later triggering segfault.
-   (merge ce83eadd9a js/realpath-pathdup-fix later to maint).
 
  * There is no need for Python only to give a few messages to the
    standard error stream, but we somehow did.
-   (merge b8686c661d ss/remote-bzr-hg-placeholder-wo-python later to maint).
 
  * The code to parse "git log -L..." command line was buggy when there
    are many ranges specified with -L; overrun of the allocated buffer
    has been fixed.
-   (merge aaae0bf787 ax/line-log-range-merge-fix later to maint).
 
  * The command-line parsing of "git log -L" copied internal data
    structures using incorrect size on ILP32 systems.
-   (merge 07f546cda5 vn/line-log-memcpy-size-fix later to maint).
 
  * "git diff --quiet" relies on the size field in diff_filespec to be
    correctly populated, but diff_populate_filespec() helper function
    made an incorrect short-cut when asked only to populate the size
    field for paths that need to go through convert_to_git() (e.g. CRLF
    conversion).
-   (merge 12426e114b jc/diff-populate-filespec-size-only-fix later to maint).
 
  * A few tests were run conditionally under (rare) conditions where
    they cannot be run (like running cvs tests under 'root' account).
-   (merge c6507484a2 ab/cond-skip-tests later to maint).
 
  * "git branch @" created refs/heads/@ as a branch, and in general the
    code that handled @{-1} and @{upstream} was a bit too loose in
    disambiguating.
-   (merge fd4692ff70 jk/interpret-branch-name later to maint).
 
  * "git fetch" that requests a commit by object name, when the other
    side does not allow such an request, failed without much
    explanation.
-   (merge d56583ded6 mm/fetch-show-error-message-on-unadvertised-object later to maint).
 
  * "git filter-branch --prune-empty" drops a single-parent commit that
    becomes a no-op, but did not drop a root commit whose tree is empty.
-   (merge 32da7467eb dp/filter-branch-prune-empty later to maint).
 
  * Recent versions of Git treats http alternates (used in dumb http
    transport) just like HTTP redirects and requires the client to
    enable following it, due to security concerns.  But we forgot to
    give a warning when we decide not to honor the alternates.
-   (merge 5cae73d5d2 ew/http-alternates-as-redirects-warning later to maint).
 
  * "git push" had a handful of codepaths that could lead to a deadlock
    when unexpected error happened, which has been fixed.
-   (merge d1a13d3fcb jk/push-deadlock-regression-fix later to maint).
+
+ * "Dumb http" transport used to misparse a nonsense http-alternates
+   response, which has been fixed.
+
+ * "git add -p <pathspec>" unnecessarily expanded the pathspec to a
+   list of individual files that matches the pathspec by running "git
+   ls-files <pathspec>", before feeding it to "git diff-index" to see
+   which paths have changes, because historically the pathspec
+   language supported by "diff-index" was weaker.  These days they are
+   equivalent and there is no reason to internally expand it.  This
+   helps both performance and avoids command line argument limit on
+   some platforms.
+   (merge 7288e12cce jk/add-i-use-pathspecs later to maint).
+
+ * "git status --porcelain" is supposed to give a stable output, but a
+   few strings were left as translatable by mistake.
+
+ * "git revert -m 0 $merge_commit" complained that reverting a merge
+   needs to say relative to which parent the reversion needs to
+   happen, as if "-m 0" weren't given.  The correct diagnosis is that
+   "-m 0" does not refer to the first parent ("-m 1" does).  This has
+   been fixed.
+
+ * Code to read submodule.<name>.ignore config did not state the
+   variable name correctly when giving an error message diagnosing
+   misconfiguration.
+
+ * Fix for NO_PTHREADS build.
+
+ * Fix for potential segv introduced in v2.11.0 and later (also
+   v2.10.2) to "git log --pickaxe-regex -S".
+
+ * A few unterminated here documents in tests were fixed, which in
+   turn revealed incorrect expectations the tests make. These tests
+   have been updated.
+
+ * Fix for NO_PTHREADS option.
+   (merge 2225e1ea20 bw/grep-recurse-submodules later to maint).
+
+ * 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").
+   (merge b1ef400eec jk/no-looking-at-dotgit-outside-repo-final later to maint).
+
+ * A few commands that recently learned the "--recurse-submodule"
+   option misbehaved when started from a subdirectory of the
+   superproject.
+   (merge b2dfeb7c00 bw/recurse-submodules-relative-fix later to maint).
+
+ * FreeBSD implementation of getcwd(3) behaved differently when an
+   intermediate directory is unreadable/unsearchable depending on the
+   length of the buffer provided, which our strbuf_getcwd() was not
+   aware of.  strbuf_getcwd() has been taught to cope with it better.
+   (merge a54e938e5b rs/freebsd-getcwd-workaround later to maint).
+
+ * A recent update to "rebase -i" stopped running hooks for the "git
+   commit" command during "reword" action, which has been fixed.
+
+ * Removing an entry from a notes tree and then looking another note
+   entry from the resulting tree using the internal notes API
+   functions did not work as expected.  No in-tree users of the API
+   has such access pattern, but it still is worth fixing.
+
+ * "git receive-pack" could have been forced to die by attempting
+   allocate an unreasonably large amount of memory with a crafted push
+   certificate; this has been fixed.
+   (merge f2214dede9 bc/push-cert-receive-fix later to maint).
+
+ * Update error handling for codepath that deals with corrupt loose
+   objects.
+   (merge 51054177b3 jk/loose-object-info-report-error later to maint).
+
+ * "git diff --submodule=diff" learned to work better in a project
+   with a submodule that in turn has its own submodules.
+   (merge 17b254cda6 sb/show-diff-for-submodule-in-diff-fix later to maint).
+
+ * Update the build dependency so that an update to /usr/bin/perl
+   etc. result in recomputation of perl.mak file.
+   (merge c59c4939c2 ab/regen-perl-mak-with-different-perl later to maint).
+
+ * "git push --recurse-submodules --push-option=<string>" learned to
+   propagate the push option recursively down to pushes in submodules.
+
+ * If a patch e-mail had its first paragraph after an in-body header
+   indented (even after a blank line after the in-body header line),
+   the indented line was mistook as a continuation of the in-body
+   header.  This has been fixed.
+   (merge fd1062e52e lt/mailinfo-in-body-header-continuation later to maint).
+
+ * Clean up fallouts from recent tightening of the set-up sequence,
+   where Git barfs when repository information is accessed without
+   first ensuring that it was started in a repository.
+   (merge bccb22cbb1 jk/no-looking-at-dotgit-outside-repo later to maint).
+
+ * "git p4" used "name-rev HEAD" when it wants to learn what branch is
+   checked out; it should use "symbolic-ref HEAD".
+   (merge eff451101d ld/p4-current-branch-fix later to maint).
+
+ * "http.proxy" set to an empty string is used to disable the usage of
+   proxy.  We broke this early last year.
+   (merge ae51d91105 sr/http-proxy-configuration-fix later to maint).
+
+ * $GIT_DIR may in some cases be normalized with all symlinks resolved
+   while "gitdir" path expansion in the pattern does not receive the
+   same treatment, leading to incorrect mismatch.  This has been fixed.
+
+ * "git submodule" script does not work well with strange pathnames.
+   Protect it from a path with slashes in them, at least.
+
+ * "git fetch-pack" was not prepared to accept ERR packet that the
+   upload-pack can send with a human-readable error message.  It
+   showed the packet contents with ERR prefix, so there was no data
+   loss, but it was redundant to say "ERR" in an error message.
+   (merge 8e2c7bef03 jt/fetch-pack-error-reporting later to maint).
+
+ * "ls-files --recurse-submodules" did not quite work well in a
+   project with nested submodules.
+
+ * gethostname(2) may not NUL terminate the buffer if hostname does
+   not fit; unfortunately there is no easy way to see if our buffer
+   was too small, but at least this will make sure we will not end up
+   using garbage past the end of the buffer.
+   (merge 5781a9a270 dt/xgethostname-nul-termination later to maint).
 
  * Other minor doc, test and build updates and code cleanups.
-   (merge 2cfa83574c mm/two-more-xstrfmt later to maint).
-   (merge b803ae4427 ps/docs-diffcore later to maint).
-   (merge bcd886d897 ew/markdown-url-in-readme later to maint).
-   (merge b2d593a779 rj/remove-unused-mktemp later to maint).
-   (merge 3255e512a8 jk/ewah-use-right-type-in-sizeof later to maint).
+   (merge df2a6e38b7 jk/pager-in-use later to maint).
+   (merge 75ec4a6cb0 ab/branch-list-doc later to maint).
+   (merge 3e5b36c637 sg/skip-prefix-in-prettify-refname later to maint).
+   (merge 2c5e2865cc jk/fast-import-cleanup later to maint).
+   (merge 4473060bc2 ab/test-readme-updates later to maint).
+   (merge 48a96972fd ab/doc-submitting later to maint).
+   (merge f5c2bc2b96 jk/make-coccicheck-detect-errors later to maint).
+   (merge c105f563d1 cc/untracked later to maint).
+   (merge 8668976b53 jc/unused-symbols later to maint).
+   (merge fba275dc93 jc/bs-t-is-not-a-tab-for-sed later to maint).
+   (merge be6ed145de mm/ls-files-s-doc later to maint).
+   (merge 60b091c679 qp/bisect-docfix later to maint).
+   (merge 47242cd103 ah/diff-files-ours-theirs-doc later to maint).
+   (merge 35ad44cbd8 sb/submodule-rm-absorb later to maint).
+   (merge 0301f1fd92 va/i18n-perl-scripts later to maint).
+   (merge 733e064d98 vn/revision-shorthand-for-side-branch-log later to maint).
+   (merge 85999743e7 tb/doc-eol-normalization later to maint).
+   (merge 0747fb49fd jk/loose-object-fsck later to maint).
+   (merge d8f4481c4f jk/quarantine-received-objects later to maint).
+   (merge 7ba1ceef95 xy/format-patch-base later to maint).
+   (merge fa1912c89a rs/misc-cppcheck-fixes later to maint).
index 3faf7eb884bc497e0823fda39734d5f5d47824ba..bc8ad00473b566261e384f24a552f9b2cd97a3f4 100644 (file)
@@ -98,12 +98,17 @@ should skip the full stop.  It is also conventional in most cases to
 prefix the first line with "area: " where the area is a filename or
 identifier for the general area of the code being modified, e.g.
 
-  . archive: ustar header checksum is computed unsigned
-  . git-cherry-pick.txt: clarify the use of revision range notation
+  . doc: clarify distinction between sign-off and pgp-signing
+  . githooks.txt: improve the intro section
 
 If in doubt which identifier to use, run "git log --no-merges" on the
 files you are modifying to see the current conventions.
 
+It's customary to start the remainder of the first line after "area: "
+with a lower-case letter. E.g. "doc: clarify...", not "doc:
+Clarify...", or "githooks.txt: improve...", not "githooks.txt:
+Improve...".
+
 The body should provide a meaningful commit message, which:
 
   . explains the problem the change tries to solve, iow, what is wrong
@@ -129,8 +134,9 @@ with the subject enclosed in a pair of double-quotes, like this:
     noticed that ...
 
 The "Copy commit summary" command of gitk can be used to obtain this
-format.
+format, or this invocation of "git show":
 
+    git show -s --date=short --pretty='format:%h ("%s", %ad)' <commit>
 
 (3) Generate your patch using Git tools out of your commits.
 
index eccc012672925a1d7967d1e2349152a0b240e85a..475e874d51550eba26a578c0ce3b17b61384fddc 100644 (file)
@@ -79,18 +79,69 @@ escape sequences) are invalid.
 Includes
 ~~~~~~~~
 
-You can include one config file from another by setting the special
+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.
+expansion. `include.path` can be given multiple times.
 
-The
-included file is expanded immediately, as if its contents had been
+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 be
-relative to the configuration file in which the include directive was
-found.  See below for examples.
+`include.path` 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.
 
+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.
+
+The condition starts with a keyword followed by a colon and some data
+whose format and meaning depends on the keyword. Supported keywords
+are:
+
+`gitdir`::
+
+       The data that follows the keyword `gitdir:` is used as a glob
+       pattern. If the location of the .git directory matches the
+       pattern, the include condition is met.
++
+The .git location may be auto-discovered, or come from `$GIT_DIR`
+environment variable. If the repository is auto discovered via a .git
+file (e.g. from submodules, or a linked worktree), the .git location
+would be the final location where the .git directory is, not where the
+.git file is.
++
+The pattern can contain standard globbing wildcards and two additional
+ones, `**/` and `/**`, that can match multiple path components. Please
+refer to linkgit:gitignore[5] for details. For convenience:
+
+ * If the pattern starts with `~/`, `~` will be substituted with the
+   content of the environment variable `HOME`.
+
+ * If the pattern starts with `./`, it is replaced with the directory
+   containing the current config file.
+
+ * If the pattern does not start with either `~/`, `./` or `/`, `**/`
+   will be automatically prepended. For example, the pattern `foo/bar`
+   becomes `**/foo/bar` and would match `/any/path/to/foo/bar`.
+
+ * If the pattern ends with `/`, `**` will be automatically added. For
+   example, the pattern `foo/` becomes `foo/**`. In other words, it
+   matches "foo" and everything inside, recursively.
+
+`gitdir/i`::
+       This is the same as `gitdir` except that matching is done
+       case-insensitively (e.g. on case-insensitive file sytems)
+
+A few more notes on matching via `gitdir` and `gitdir/i`:
+
+ * Symlinks in `$GIT_DIR` are not resolved before matching.
+
+ * Note that "../" is not special and will match literally, which is
+   unlikely what you want.
 
 Example
 ~~~~~~~
@@ -119,6 +170,17 @@ Example
                path = foo ; expand "foo" relative to the current file
                path = ~/foo ; expand "foo" in your `$HOME` directory
 
+       ; include if $GIT_DIR is /path/to/foo/.git
+       [includeIf "gitdir:/path/to/foo/.git"]
+               path = /path/to/foo.inc
+
+       ; include for all repositories inside /path/to/group
+       [includeIf "gitdir:/path/to/group/"]
+               path = /path/to/foo.inc
+
+       ; include for all repositories inside $HOME/to/group
+       [includeIf "gitdir:~/to/group/"]
+               path = /path/to/foo.inc
 
 Values
 ~~~~~~
@@ -675,13 +737,13 @@ alternative to having an `init.templateDir` where you've changed
 default hooks.
 
 core.editor::
-       Commands such as `commit` and `tag` that lets you edit
-       messages by launching an editor uses the value of this
+       Commands such as `commit` and `tag` that let you edit
+       messages by launching an editor use the value of this
        variable when it is set, and the environment variable
        `GIT_EDITOR` is not set.  See linkgit:git-var[1].
 
 core.commentChar::
-       Commands such as `commit` and `tag` that lets you edit
+       Commands such as `commit` and `tag` that let you edit
        messages consider a line that begins with this character
        commented, and removes them after the editor returns
        (default '#').
@@ -2459,6 +2521,8 @@ push.default::
   pushing to the same repository you would normally pull from
   (i.e. central workflow).
 
+* `tracking` - This is a deprecated synonym for `upstream`.
+
 * `simple` - in centralized workflow, work like `upstream` with an
   added safety to refuse to push if the upstream branch's name is
   different from the local one.
@@ -2949,8 +3013,9 @@ submodule.<name>.url::
        The URL for a submodule. This variable is copied from the .gitmodules
        file to the git config via 'git submodule init'. The user can change
        the configured URL before obtaining the submodule via 'git submodule
-       update'. After obtaining the submodule, the presence of this variable
-       is used as a sign whether the submodule is of interest to git commands.
+       update'. If neither submodule.<name>.active or submodule.active are
+       set, the presence of this variable is used as a fallback to indicate
+       whether the submodule is of interest to git commands.
        See linkgit:git-submodule[1] and linkgit:gitmodules[5] for details.
 
 submodule.<name>.update::
@@ -2988,6 +3053,16 @@ submodule.<name>.ignore::
        "--ignore-submodules" option. The 'git submodule' commands are not
        affected by this setting.
 
+submodule.<name>.active::
+       Boolean value indicating if the submodule is of interest to git
+       commands.  This config option takes precedence over the
+       submodule.active config option.
+
+submodule.active::
+       A repeated field which contains a pathspec used to match against a
+       submodule's path to determine if the submodule is of interest to git
+       commands.
+
 submodule.fetchJobs::
        Specifies how many submodules are fetched/cloned at the same time.
        A positive integer allows up to that number of submodules fetched
index bdd915a66b481dccf9ae6d601b5497ceb6b3b3d7..6c42abf070df93187e11dd31d263b199346cace2 100644 (file)
@@ -137,7 +137,7 @@ respectively, in place of "good" and "bad". (But note that you cannot
 mix "good" and "bad" with "old" and "new" in a single session.)
 
 In this more general usage, you provide `git bisect` with a "new"
-commit has some property and an "old" commit that doesn't have that
+commit that has some property and an "old" commit that doesn't have that
 property. Each time `git bisect` checks out a commit, you test if that
 commit has the property. If it does, mark the commit as "new";
 otherwise, mark it as "old". When the bisection is done, `git bisect`
index 092f1bcf9f89f124a2d3c80eadeab5b4a63db941..81bd0a7b7741f175cf7a99e2aa9cbcacf42da78e 100644 (file)
@@ -10,8 +10,9 @@ SYNOPSIS
 [verse]
 'git branch' [--color[=<when>] | --no-color] [-r | -a]
        [--list] [-v [--abbrev=<length> | --no-abbrev]]
-       [--column[=<options>] | --no-column]
-       [(--merged | --no-merged | --contains) [<commit>]] [--sort=<key>]
+       [--column[=<options>] | --no-column] [--sort=<key>]
+       [(--merged | --no-merged) [<commit>]]
+       [--contains [<commit]] [--no-contains [<commit>]]
        [--points-at <object>] [--format=<format>] [<pattern>...]
 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>]
 'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
@@ -35,11 +36,12 @@ as branch creation.
 
 With `--contains`, shows only the branches that contain the named commit
 (in other words, the branches whose tip commits are descendants of the
-named commit).  With `--merged`, only branches merged into the named
-commit (i.e. the branches whose tip commits are reachable from the named
-commit) will be listed.  With `--no-merged` only branches not merged into
-the named commit will be listed.  If the <commit> argument is missing it
-defaults to `HEAD` (i.e. the tip of the current branch).
+named commit), `--no-contains` inverts it. With `--merged`, only branches
+merged into the named commit (i.e. the branches whose tip commits are
+reachable from the named commit) will be listed.  With `--no-merged` only
+branches not merged into the named commit will be listed.  If the <commit>
+argument is missing it defaults to `HEAD` (i.e. the tip of the current
+branch).
 
 The command's second form creates a new branch head named <branchname>
 which points to the current `HEAD`, or <start-point> if given.
@@ -142,8 +144,13 @@ This option is only applicable in non-verbose mode.
        List both remote-tracking branches and local branches.
 
 --list::
-       Activate the list mode. `git branch <pattern>` would try to create a branch,
-       use `git branch --list <pattern>` to list matching branches.
+       List branches.  With optional `<pattern>...`, e.g. `git
+       branch --list 'maint-*'`, list only the branches that match
+       the pattern(s).
++
+This should not be confused with `git branch -l <branchname>`,
+which creates a branch named `<branchname>` with a reflog.
+See `--create-reflog` above for details.
 
 -v::
 -vv::
@@ -213,13 +220,19 @@ start-point is either a local or remote-tracking branch.
        Only list branches which contain the specified commit (HEAD
        if not specified). Implies `--list`.
 
+--no-contains [<commit>]::
+       Only list branches which don't contain the specified commit
+       (HEAD if not specified). Implies `--list`.
+
 --merged [<commit>]::
        Only list branches whose tips are reachable from the
-       specified commit (HEAD if not specified). Implies `--list`.
+       specified commit (HEAD if not specified). Implies `--list`,
+       incompatible with `--no-merged`.
 
 --no-merged [<commit>]::
        Only list branches whose tips are not reachable from the
-       specified commit (HEAD if not specified). Implies `--list`.
+       specified commit (HEAD if not specified). Implies `--list`,
+       incompatible with `--merged`.
 
 <branchname>::
        The name of the branch to create or delete.
@@ -296,13 +309,16 @@ If you are creating a branch that you want to checkout immediately, it is
 easier to use the git checkout command with its `-b` option to create
 a branch and check it out with a single command.
 
-The options `--contains`, `--merged` and `--no-merged` serve three related
-but different purposes:
+The options `--contains`, `--no-contains`, `--merged` and `--no-merged`
+serve four related but different purposes:
 
 - `--contains <commit>` is used to find all branches which will need
   special attention if <commit> were to be rebased or amended, since those
   branches contain the specified <commit>.
 
+- `--no-contains <commit>` is the inverse of that, i.e. branches that don't
+  contain the specified <commit>.
+
 - `--merged` is used to find all branches which can be safely deleted,
   since those branches are fully contained by HEAD.
 
index 8e2c0662ddd72c1bc0765aadbbafd22b62b5adf6..d6399c0af86bb84cd86b82500ce706cbcd93cd93 100644 (file)
@@ -256,6 +256,13 @@ section of linkgit:git-add[1] to learn how to operate the `--patch` mode.
        out anyway. In other words, the ref can be held by more than one
        worktree.
 
+--[no-]recurse-submodules::
+       Using --recurse-submodules will update the content of all initialized
+       submodules according to the commit recorded in the superproject. If
+       local modifications in a submodule would be overwritten the checkout
+       will fail unless `-f` is used. If nothing (or --no-recurse-submodules)
+       is used, the work trees of submodules will not be updated.
+
 <branch>::
        Branch to checkout; if it refers to a branch (i.e., a name that,
        when prepended with "refs/heads/", is a valid ref), then that
index 35cc34b2fb9a0e696eb86e208416fa93aae3c0c5..30052cce49947914f71b16aa0474bfaa36d13b09 100644 (file)
@@ -14,7 +14,7 @@ SYNOPSIS
          [-o <name>] [-b <name>] [-u <upload-pack>] [--reference <repository>]
          [--dissociate] [--separate-git-dir <git dir>]
          [--depth <depth>] [--[no-]single-branch]
-         [--recursive | --recurse-submodules] [--[no-]shallow-submodules]
+         [--recurse-submodules] [--[no-]shallow-submodules]
          [--jobs <n>] [--] <repository> [<directory>]
 
 DESCRIPTION
@@ -215,10 +215,14 @@ 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.
 
---recursive::
---recurse-submodules::
-       After the clone is created, initialize all submodules within,
-       using their default settings. This is equivalent to running
+--recurse-submodules[=<pathspec]::
+       After the clone is created, initialize and clone submodules
+       within based on the provided pathspec.  If no pathspec is
+       provided, all submodules are initialized and cloned.
+       Submodules are initialized and cloned using their default
+       settings.  The resulting clone has `submodule.active` set to
+       the provided pathspec, or "." (meaning all submodules) if no
+       pathspec is provided.  This is equivalent to running
        `git submodule update --init --recursive` immediately after
        the clone is finished. This option is ignored if the cloned
        repository does not have a worktree/checkout (i.e. if any of
index 25dcdcc2890f0a6814c0000c3c8970e02c0717f8..ed0f5b94b3f200676aedb312391a2dd8fde7325d 100644 (file)
@@ -463,7 +463,7 @@ order).  See linkgit:git-var[1] for details.
 HOOKS
 -----
 This command can run `commit-msg`, `prepare-commit-msg`, `pre-commit`,
-and `post-commit` hooks.  See linkgit:githooks[5] for more
+`post-commit` and `post-rewrite` hooks.  See linkgit:githooks[5] for more
 information.
 
 FILES
index 96208f822e0995f97664423038abe1f406431986..2b8582639332acb4d3e81273c6a90163a3a8b378 100644 (file)
@@ -33,10 +33,13 @@ OPTIONS
 --socket <path>::
 
        Use `<path>` to contact a running cache daemon (or start a new
-       cache daemon if one is not started). Defaults to
-       `~/.git-credential-cache/socket`. If your home directory is on a
-       network-mounted filesystem, you may need to change this to a
-       local filesystem. You must specify an absolute path.
+       cache daemon if one is not started).
+       Defaults to `$XDG_CACHE_HOME/git/credential/socket` unless
+       `~/.git-credential-cache/` exists in which case
+       `~/.git-credential-cache/socket` is used instead.
+       If your home directory is on a network-mounted filesystem, you
+       may need to change this to a local filesystem. You must specify
+       an absolute path.
 
 CONTROLLING THE DAEMON
 ----------------------
index 8755f3af7bcd16b564fdd01563e36d3cc4103c07..26f19d3b072aa358043fbf79f3057a11aee2fdb2 100644 (file)
@@ -30,9 +30,14 @@ OPTIONS
        Commit-ish object names to describe.  Defaults to HEAD if omitted.
 
 --dirty[=<mark>]::
-       Describe the working tree.
-       It means describe HEAD and appends <mark> (`-dirty` by
-       default) if the working tree is dirty.
+--broken[=<mark>]::
+       Describe the state of the working tree.  When the working
+       tree matches HEAD, the output is the same as "git describe
+       HEAD".  If the working tree has local modification "-dirty"
+       is appended to it.  If a repository is corrupt and Git
+       cannot determine if there is local modification, Git will
+       error out, unless `--broken' is given, which appends
+       the suffix "-broken" instead.
 
 --all::
        Instead of using only the annotated tags, use any ref
index bbab35fcaff35ccd5459251550924a42ce2c871e..b0c1bb95c83b8e8e6cf6fa863e74108e3e5fc35f 100644 (file)
@@ -97,6 +97,20 @@ OPTIONS
 :git-diff: 1
 include::diff-options.txt[]
 
+-1 --base::
+-2 --ours::
+-3 --theirs::
+       Compare the working tree with the "base" version (stage #1),
+       "our branch" (stage #2) or "their branch" (stage #3).  The
+       index contains these stages only for unmerged entries i.e.
+       while resolving conflicts.  See linkgit:git-read-tree[1]
+       section "3-Way Merge" for detailed information.
+
+-0::
+       Omit diff output for unmerged entries and just show
+       "Unmerged".  Can be used only when comparing the working tree
+       with the index.
+
 <path>...::
        The <paths> parameters, when given, are used to limit
        the diff to the named paths (you can give directory
index 111e1be6f54f73f25ef6715a17410e461724519a..03e187a105b1bd5ffa6e615c2dc9199f0b803e44 100644 (file)
@@ -11,7 +11,7 @@ SYNOPSIS
 'git for-each-ref' [--count=<count>] [--shell|--perl|--python|--tcl]
                   [(--sort=<key>)...] [--format=<format>] [<pattern>...]
                   [--points-at <object>] [(--merged | --no-merged) [<object>]]
-                  [--contains [<object>]]
+                  [--contains [<object>]] [--no-contains [<object>]]
 
 DESCRIPTION
 -----------
@@ -69,16 +69,22 @@ OPTIONS
 
 --merged [<object>]::
        Only list refs whose tips are reachable from the
-       specified commit (HEAD if not specified).
+       specified commit (HEAD if not specified),
+       incompatible with `--no-merged`.
 
 --no-merged [<object>]::
        Only list refs whose tips are not reachable from the
-       specified commit (HEAD if not specified).
+       specified commit (HEAD if not specified),
+       incompatible with `--merged`.
 
 --contains [<object>]::
        Only list refs which contain the specified commit (HEAD if not
        specified).
 
+--no-contains [<object>]::
+       Only list refs which don't contain the specified commit (HEAD
+       if not specified).
+
 --ignore-case::
        Sorting and filtering refs are case insensitive.
 
index 9b200b379bbc980f37033c68342a287637750583..c890328b02ec4c3a28aaeb8aa057d327c75b9967 100644 (file)
@@ -239,7 +239,7 @@ keeping them as Git notes allows them to be maintained between versions
 of the patch series (but see the discussion of the `notes.rewrite`
 configuration options in linkgit:git-notes[1] to use this workflow).
 
---[no]-signature=<signature>::
+--[no-]signature=<signature>::
        Add a signature to each message produced. Per RFC 3676 the signature
        is separated from the body by a line with '-- ' on it. If the
        signature option is omitted the signature defaults to the Git version
@@ -557,7 +557,7 @@ series A, B, C, the history would be like:
 ................................................
 
 With `git format-patch --base=P -3 C` (or variants thereof, e.g. with
-`--cover-letter` of using `Z..C` instead of `-3 C` to specify the
+`--cover-letter` or using `Z..C` instead of `-3 C` to specify the
 range), the base tree information block is shown at the end of the
 first message the command outputs (either the first patch, or the
 cover letter), like this:
index 1cab703f73611260845ef5e9926fe377e729b934..d153c17e0660d8ab9edb0b10a53172007d355510 100644 (file)
@@ -57,7 +57,7 @@ OPTIONS
 
 -s::
 --stage::
-       Show staged contents' object name, mode bits and stage number in the output.
+       Show staged contents' mode bits, object name and stage number in the output.
 
 --directory::
        If a whole directory is classified as "other", show just its
index ca3c27b88a4ea0dde17ed2e7f260ca6158a50073..04fdd8cf086db6413a01421c306a80c9583f7fa4 100644 (file)
@@ -13,7 +13,6 @@ SYNOPSIS
        [-s <strategy>] [-X <strategy-option>] [-S[<keyid>]]
        [--[no-]allow-unrelated-histories]
        [--[no-]rerere-autoupdate] [-m <msg>] [<commit>...]
-'git merge' <msg> HEAD <commit>...
 'git merge' --abort
 'git merge' --continue
 
@@ -46,11 +45,7 @@ a log message from the user describing the changes.
     D---E---F---G---H master
 ------------
 
-The second syntax (<msg> `HEAD` <commit>...) is supported for
-historical reasons.  Do not use it from the command line or in
-new scripts.  It is the same as `git merge -m <msg> <commit>...`.
-
-The third syntax ("`git merge --abort`") can only be run after the
+The second syntax ("`git merge --abort`") can only be run after the
 merge has resulted in conflicts. 'git merge --abort' will abort the
 merge process and try to reconstruct the pre-merge state. However,
 if there were uncommitted changes when the merge started (and
index fa1d557e5b98eb6dc40e3baa65964138b4cc0621..ed9d63ef4a35fa5ba70b1b7f1ffd393f517be030 100644 (file)
@@ -115,6 +115,12 @@ OPTIONS
        directories the index file and index output file are
        located in.
 
+--[no-]recurse-submodules::
+       Using --recurse-submodules will update the content of all initialized
+       submodules according to the commit recorded in the superproject by
+       calling read-tree recursively, also setting the submodules HEAD to be
+       detached at that commit.
+
 --no-sparse-checkout::
        Disable sparse checkout support even if `core.sparseCheckout`
        is true.
index 67d48e68831561303c4f39f46bf105371ed0d916..53f4e144444ae7b30ce2634c0f364400927b27d2 100644 (file)
@@ -370,6 +370,11 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
        of the rebased commits (see linkgit:git-am[1]).
        Incompatible with the --interactive option.
 
+--signoff::
+       This flag is passed to 'git am' to sign off all the rebased
+       commits (see linkgit:git-am[1]). Incompatible with the
+       --interactive option.
+
 -i::
 --interactive::
        Make a list of the commits which are about to be rebased.  Let the
index 0ccd5fbc781deb3adcca4d28ec8a4ed0d9db9977..86a4b32f0f1cbb8b69ff8b935edade6f14e7d5e2 100644 (file)
@@ -114,6 +114,8 @@ will be performed, and the update, post-receive and post-update
 hooks will not be invoked either.  This can be useful to quickly
 bail out if the update is not to be supported.
 
+See the notes on the quarantine environment below.
+
 update Hook
 -----------
 Before each ref is updated, if $GIT_DIR/hooks/update file exists
@@ -214,6 +216,33 @@ if the repository is packed and is served via a dumb transport.
        exec git update-server-info
 
 
+Quarantine Environment
+----------------------
+
+When `receive-pack` takes in objects, they are placed into a temporary
+"quarantine" directory within the `$GIT_DIR/objects` directory and
+migrated into the main object store only after the `pre-receive` hook
+has completed. If the push fails before then, the temporary directory is
+removed entirely.
+
+This has a few user-visible effects and caveats:
+
+  1. Pushes which fail due to problems with the incoming pack, missing
+     objects, or due to the `pre-receive` hook will not leave any
+     on-disk data. This is usually helpful to prevent repeated failed
+     pushes from filling up your disk, but can make debugging more
+     challenging.
+
+  2. Any objects created by the `pre-receive` hook will be created in
+     the quarantine directory (and migrated only if it succeeds).
+
+  3. The `pre-receive` hook MUST NOT update any refs to point to
+     quarantined objects. Other programs accessing the repository will
+     not be able to see the objects (and if the pre-receive hook fails,
+     those refs would become corrupted). For safety, any ref updates
+     from within `pre-receive` are automatically rejected.
+
+
 SEE ALSO
 --------
 linkgit:git-send-pack[1], linkgit:gitnamespaces[7]
index 642d0ef199c72160ca74238122fafdbdfee9a0c7..9d66166f69d93e544c949ad80fe0f432fa3f1ffd 100644 (file)
@@ -89,7 +89,7 @@ See the CONFIGURATION section for `sendemail.multiEdit`.
        reply to the given Message-Id, which avoids breaking threads to
        provide a new patch series.
        The second and subsequent emails will be sent as replies according to
-       the `--[no]-chain-reply-to` setting.
+       the `--[no-]chain-reply-to` setting.
 +
 So for example when `--thread` and `--no-chain-reply-to` are specified, the
 second and subsequent patches will be replies to the first one like in the
index a831dd0288306d3355bb5607bc6f36229563c8f7..966abb0df807c79714a18d8b106390caae45cf89 100644 (file)
@@ -81,6 +81,12 @@ be in a separate packet, and the list must end with a flush packet.
        will also fail if the actual call to `gpg --sign` fails.  See
        linkgit:git-receive-pack[1] for the details on the receiving end.
 
+--push-option=<string>::
+       Pass the specified string as a push option for consumption by
+       hooks on the server side.  If the server doesn't support push
+       options, error out.  See linkgit:git-push[1] and
+       linkgit:githooks[5] for details.
+
 <host>::
        A remote host to house the repository.  When this
        part is specified, 'git-receive-pack' is invoked via
index ba873657cf2f9d139d2ddb7c19e6865cc159a64d..d70abc6afe3aa40aaaab2e055ee75d8ee22cdb69 100644 (file)
@@ -181,6 +181,17 @@ in which case `XY` are `!!`.
     !           !    ignored
     -------------------------------------------------
 
+Submodules have more state and instead report
+               M    the submodule has a different HEAD than
+                    recorded in the index
+               m    the submodule has modified content
+               ?    the submodule has untracked files
+since modified content or untracked files in a submodule cannot be added
+via `git add` in the superproject to prepare a commit.
+
+'m' and '?' are applied recursively. For example if a nested submodule
+in a submodule contains an untracked file, this is reported as '?' as well.
+
 If -b is used the short-format status is preceded by a line
 
     ## branchname tracking info
@@ -210,6 +221,8 @@ field from the first filename).  Third, filenames containing special
 characters are not specially formatted; no quoting or
 backslash-escaping is performed.
 
+Any submodule changes are reported as modified `M` instead of `m` or single `?`.
+
 Porcelain Format Version 2
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
index e05d0cddefe16194f87b00918f057bf4fb481855..74bc6200d564c6aa72183e393f7c7d2e0c00bf7d 100644 (file)
@@ -129,7 +129,9 @@ init [--] [<path>...]::
        repository will be assumed to be upstream.
 +
 Optional <path> arguments limit which submodules will be initialized.
-If no path is specified, all submodules are initialized.
+If no path is specified and submodule.active has been configured, submodules
+configured to be active will be initialized, otherwise all submodules are
+initialized.
 +
 When present, it will also copy the value of `submodule.$name.update`.
 This command does not alter existing information in .git/config.
index 525737a5d891bb4a8b4df92c26a20550a927a0b9..f8a0b787f47d6e752761fa2a442d4ebcb300e2ed 100644 (file)
@@ -12,9 +12,10 @@ 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>] [--points-at <object>]
-       [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
-       [--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]
+'git tag' [-n[<num>]] -l [--contains <commit>] [--contains <commit>]
+       [--points-at <object>] [--column[=<options>] | --no-column]
+       [--create-reflog] [--sort=<key>] [--format=<format>]
+       [--[no-]merged [<commit>]] [<pattern>...]
 'git tag' -v [--format=<format>] <tagname>...
 
 DESCRIPTION
@@ -82,18 +83,24 @@ OPTIONS
 
 -n<num>::
        <num> specifies how many lines from the annotation, if any,
-       are printed when using -l.
-       The default is not to print any annotation lines.
-       If no number is given to `-n`, only the first line is printed.
-       If the tag is not annotated, the commit message is displayed instead.
-
--l <pattern>::
---list <pattern>::
-       List tags with names that match the given pattern (or all if no
-       pattern is given).  Running "git tag" without arguments also
-       lists all tags. The pattern is a shell wildcard (i.e., matched
-       using fnmatch(3)).  Multiple patterns may be given; if any of
-       them matches, the tag is shown.
+       are printed when using -l. Implies `--list`.
++
+The default is not to print any annotation lines.
+If no number is given to `-n`, only the first line is printed.
+If the tag is not annotated, the commit message is displayed instead.
+
+-l::
+--list::
+       List tags. With optional `<pattern>...`, e.g. `git tag --list
+       'v-*'`, list only the tags that match the pattern(s).
++
+Running "git tag" without arguments also lists all tags. The pattern
+is a shell wildcard (i.e., matched using fnmatch(3)). Multiple
+patterns may be given; if any of them matches, the tag is shown.
++
+This option is implicitly supplied if any other list-like option such
+as `--contains` is provided. See the documentation for each of those
+options for details.
 
 --sort=<key>::
        Sort based on the key given.  Prefix `-` to sort in
@@ -122,10 +129,23 @@ This option is only applicable when listing tags without annotation lines.
 
 --contains [<commit>]::
        Only list tags which contain the specified commit (HEAD if not
-       specified).
+       specified). Implies `--list`.
+
+--no-contains [<commit>]::
+       Only list tags which don't contain the specified commit (HEAD if
+       not specified). Implies `--list`.
+
+--merged [<commit>]::
+       Only list tags whose commits are reachable from the specified
+       commit (`HEAD` if not specified), incompatible with `--no-merged`.
+
+--no-merged [<commit>]::
+       Only list tags whose commits are not reachable from the specified
+       commit (`HEAD` if not specified), incompatible with `--merged`.
 
 --points-at <object>::
-       Only list tags of the given object.
+       Only list tags of the given object (HEAD if not
+       specified). Implies `--list`.
 
 -m <msg>::
 --message=<msg>::
@@ -173,11 +193,6 @@ This option is only applicable when listing tags without annotation lines.
        that of linkgit:git-for-each-ref[1].  When unspecified,
        defaults to `%(refname:strip=2)`.
 
---[no-]merged [<commit>]::
-       Only list tags whose tips are reachable, or not reachable
-       if `--no-merged` is used, from the specified commit (`HEAD`
-       if not specified).
-
 CONFIGURATION
 -------------
 By default, 'git tag' in sign-with-default mode (-s) will use your
index df0941d456b0fa1ab3be4f459f73edd3145ffb32..ecc1bb4bd7c3e6adc82c9955817b532bedadc453 100644 (file)
@@ -44,9 +44,11 @@ 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.12.0/git.html[documentation for release 2.12.0]
+* link:v2.12.2/git.html[documentation for release 2.12.2]
 
 * release notes for
+  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]
index a53d093ca1666e6f84f229f94a3e4b7b1ed6c899..4736483865ee56c3cf23f41985e2064fbef0cfea 100644 (file)
@@ -229,11 +229,9 @@ From a clean working directory:
 
 -------------------------------------------------
 $ echo "* text=auto" >.gitattributes
-$ rm .git/index     # Remove the index to force Git to
-$ git reset         # re-scan the working directory
+$ rm .git/index     # Remove the index to re-scan the working directory
+$ git add .
 $ git status        # Show files that will be normalized
-$ git add -u
-$ git add .gitattributes
 $ git commit -m "Introduce end-of-line normalization"
 -------------------------------------------------
 
index 9565dc3fda47d7c8a7290132c347b5a6f0d2422e..32343ae295e7c94a70f67e9abca542f22d992102 100644 (file)
@@ -256,6 +256,9 @@ environment variables will not be set. If the client selects
 to use push options, but doesn't transmit any, the count variable
 will be set to zero, `GIT_PUSH_OPTION_COUNT=0`.
 
+See the section on "Quarantine Environment" in
+linkgit:git-receive-pack[1] for some caveats.
+
 [[update]]
 update
 ~~~~~~
index ba11b9c95e3a6efef461e5cadc4834ce232896b1..61277469c874933fbc21f7d64a3687374e9324c2 100644 (file)
@@ -96,7 +96,8 @@ some output processing may assume ref names in UTF-8.
   refers to the branch that the branch specified by branchname is set to build on
   top of (configured with `branch.<name>.remote` and
   `branch.<name>.merge`).  A missing branchname defaults to the
-  current one.
+  current one. These suffixes are also accepted when spelled in uppercase, and
+  they mean the same thing no matter the case.
 
 '<branchname>@\{push\}', e.g. 'master@\{push\}', '@\{push\}'::
   The suffix '@\{push}' reports the branch "where we would push to" if
@@ -122,6 +123,9 @@ refs/remotes/myfork/mybranch
 Note in the example that we set up a triangular workflow, where we pull
 from one location and push to another. In a non-triangular workflow,
 '@\{push}' is the same as '@\{upstream}', and there is no need for it.
++
+This suffix is also accepted when spelled in uppercase, and means the same
+thing no matter the case.
 
 '<rev>{caret}', e.g. 'HEAD{caret}, v1.5.1{caret}0'::
   A suffix '{caret}' to a revision parameter means the first parent of
@@ -291,7 +295,7 @@ The 'r1{caret}@' notation means all parents of 'r1'.
 The 'r1{caret}!' notation includes commit 'r1' but excludes all of its parents.
 By itself, this notation denotes the single commit 'r1'.
 
-The '<rev>{caret}-{<n>}' notation includes '<rev>' but excludes the <n>th
+The '<rev>{caret}-<n>' notation includes '<rev>' but excludes the <n>th
 parent (i.e. a shorthand for '<rev>{caret}<n>..<rev>'), with '<n>' = 1 if
 not given. This is typically useful for merge commits where you
 can just pass '<commit>{caret}-' to get all the commits in the branch
@@ -333,7 +337,7 @@ Revision Range Summary
   as giving commit '<rev>' and then all its parents prefixed with
   '{caret}' to exclude them (and their ancestors).
 
-'<rev>{caret}-{<n>}', e.g. 'HEAD{caret}-, HEAD{caret}-2'::
+'<rev>{caret}-<n>', e.g. 'HEAD{caret}-, HEAD{caret}-2'::
        Equivalent to '<rev>{caret}<n>..<rev>', with '<n>' = 1 if not
        given.
 
index a3f020cd9ef6a5110a1ca439403e525377975b72..ccc634bbd754f6e0bab80fb3ccf1cc42afc5b829 100644 (file)
@@ -21,6 +21,9 @@ that the hashmap is initialized. It may also be useful for statistical purposes
 `cmpfn` stores the comparison function specified in `hashmap_init()`. In
 advanced scenarios, it may be useful to change this, e.g. to switch between
 case-sensitive and case-insensitive lookup.
++
+When `disallow_rehash` is set, automatic rehashes are prevented during inserts
+and deletes.
 
 `struct hashmap_entry`::
 
@@ -57,6 +60,7 @@ Functions
 `unsigned int strihash(const char *buf)`::
 `unsigned int memhash(const void *buf, size_t len)`::
 `unsigned int memihash(const void *buf, size_t len)`::
+`unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len)`::
 
        Ready-to-use hash functions for strings, using the FNV-1 algorithm (see
        http://www.isthe.com/chongo/tech/comp/fnv).
@@ -65,6 +69,9 @@ Functions
 `memihash` operate on arbitrary-length memory.
 +
 `strihash` and `memihash` are case insensitive versions.
++
+`memihash_cont` is a variant of `memihash` that allows a computation to be
+continued with another chunk of data.
 
 `unsigned int sha1hash(const unsigned char *sha1)`::
 
@@ -184,6 +191,21 @@ passed to `hashmap_cmp_fn` to decide whether the entry matches the key.
 +
 Returns the removed entry, or NULL if not found.
 
+`void hashmap_disallow_rehash(struct hashmap *map, unsigned value)`::
+
+       Disallow/allow automatic rehashing of the hashmap during inserts
+       and deletes.
++
+This is useful if the caller knows that the hashmap will be accessed
+by multiple threads.
++
+The caller is still responsible for any necessary locking; this simply
+prevents unexpected rehashing.  The caller is also responsible for properly
+sizing the initial hashmap to ensure good performance.
++
+A call to allow rehashing does not force a rehash; that might happen
+with the next insert or delete.
+
 `void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter)`::
 `void *hashmap_iter_next(struct hashmap_iter *iter)`::
 `void *hashmap_iter_first(struct hashmap *map, struct hashmap_iter *iter)`::
diff --git a/Documentation/technical/api-oid-array.txt b/Documentation/technical/api-oid-array.txt
new file mode 100644 (file)
index 0000000..b0c11f8
--- /dev/null
@@ -0,0 +1,80 @@
+oid-array API
+==============
+
+The oid-array API provides storage and manipulation of sets of object
+identifiers. The emphasis is on storage and processing efficiency,
+making them suitable for large lists. Note that the ordering of items is
+not preserved over some operations.
+
+Data Structures
+---------------
+
+`struct oid_array`::
+
+       A single array of object IDs. This should be initialized by
+       assignment from `OID_ARRAY_INIT`.  The `oid` member contains
+       the actual data. The `nr` member contains the number of items in
+       the set.  The `alloc` and `sorted` members are used internally,
+       and should not be needed by API callers.
+
+Functions
+---------
+
+`oid_array_append`::
+       Add an item to the set. The object ID will be placed at the end of
+       the array (but note that some operations below may lose this
+       ordering).
+
+`oid_array_lookup`::
+       Perform a binary search of the array for a specific object ID.
+       If found, returns the offset (in number of elements) of the
+       object ID. If not found, returns a negative integer. If the array
+       is not sorted, this function has the side effect of sorting it.
+
+`oid_array_clear`::
+       Free all memory associated with the array and return it to the
+       initial, empty state.
+
+`oid_array_for_each_unique`::
+       Efficiently iterate over each unique element of the list,
+       executing the callback function for each one. If the array is
+       not sorted, this function has the side effect of sorting it. If
+       the callback returns a non-zero value, the iteration ends
+       immediately and the callback's return is propagated; otherwise,
+       0 is returned.
+
+Examples
+--------
+
+-----------------------------------------
+int print_callback(const struct object_id *oid,
+                   void *data)
+{
+       printf("%s\n", oid_to_hex(oid));
+       return 0; /* always continue */
+}
+
+void some_func(void)
+{
+       struct sha1_array hashes = OID_ARRAY_INIT;
+       struct object_id oid;
+
+       /* Read objects into our set */
+       while (read_object_from_stdin(oid.hash))
+               oid_array_append(&hashes, &oid);
+
+       /* Check if some objects are in our set */
+       while (read_object_from_stdin(oid.hash)) {
+               if (oid_array_lookup(&hashes, &oid) >= 0)
+                       printf("it's in there!\n");
+
+       /*
+        * Print the unique set of objects. We could also have
+        * avoided adding duplicate objects in the first place,
+        * but we would end up re-sorting the array repeatedly.
+        * Instead, this will sort once and then skip duplicates
+        * in linear time.
+        */
+       oid_array_for_each_unique(&hashes, print_callback, NULL);
+}
+-----------------------------------------
diff --git a/Documentation/technical/api-sha1-array.txt b/Documentation/technical/api-sha1-array.txt
deleted file mode 100644 (file)
index dcc5294..0000000
+++ /dev/null
@@ -1,80 +0,0 @@
-sha1-array API
-==============
-
-The sha1-array API provides storage and manipulation of sets of SHA-1
-identifiers. The emphasis is on storage and processing efficiency,
-making them suitable for large lists. Note that the ordering of items is
-not preserved over some operations.
-
-Data Structures
----------------
-
-`struct sha1_array`::
-
-       A single array of SHA-1 hashes. This should be initialized by
-       assignment from `SHA1_ARRAY_INIT`.  The `sha1` member contains
-       the actual data. The `nr` member contains the number of items in
-       the set.  The `alloc` and `sorted` members are used internally,
-       and should not be needed by API callers.
-
-Functions
----------
-
-`sha1_array_append`::
-       Add an item to the set. The sha1 will be placed at the end of
-       the array (but note that some operations below may lose this
-       ordering).
-
-`sha1_array_lookup`::
-       Perform a binary search of the array for a specific sha1.
-       If found, returns the offset (in number of elements) of the
-       sha1. If not found, returns a negative integer. If the array is
-       not sorted, this function has the side effect of sorting it.
-
-`sha1_array_clear`::
-       Free all memory associated with the array and return it to the
-       initial, empty state.
-
-`sha1_array_for_each_unique`::
-       Efficiently iterate over each unique element of the list,
-       executing the callback function for each one. If the array is
-       not sorted, this function has the side effect of sorting it. If
-       the callback returns a non-zero value, the iteration ends
-       immediately and the callback's return is propagated; otherwise,
-       0 is returned.
-
-Examples
---------
-
------------------------------------------
-int print_callback(const unsigned char sha1[20],
-                   void *data)
-{
-       printf("%s\n", sha1_to_hex(sha1));
-       return 0; /* always continue */
-}
-
-void some_func(void)
-{
-       struct sha1_array hashes = SHA1_ARRAY_INIT;
-       unsigned char sha1[20];
-
-       /* Read objects into our set */
-       while (read_object_from_stdin(sha1))
-               sha1_array_append(&hashes, sha1);
-
-       /* Check if some objects are in our set */
-       while (read_object_from_stdin(sha1)) {
-               if (sha1_array_lookup(&hashes, sha1) >= 0)
-                       printf("it's in there!\n");
-
-       /*
-        * Print the unique set of objects. We could also have
-        * avoided adding duplicate objects in the first place,
-        * but we would end up re-sorting the array repeatedly.
-        * Instead, this will sort once and then skip duplicates
-        * in linear time.
-        */
-       sha1_array_for_each_unique(&hashes, print_callback, NULL);
-}
------------------------------------------
index c59ac9936a89e5e195b9a75bd4fd5d64a2385ffa..5b0ba3ef20144d1676cedbd8435e7bffddcde2f7 100644 (file)
@@ -351,14 +351,19 @@ ACK after 'done' if there is at least one common base and multi_ack or
 multi_ack_detailed is enabled. The server always sends NAK after 'done'
 if there is no common base found.
 
+Instead of 'ACK' or 'NAK', the server may send an error message (for
+example, if it does not recognize an object in a 'want' line received
+from the client).
+
 Then the server will start sending its packfile data.
 
 ----
-  server-response = *ack_multi ack / nak
+  server-response = *ack_multi ack / nak / error-line
   ack_multi       = PKT-LINE("ACK" SP obj-id ack_status)
   ack_status      = "continue" / "common" / "ready"
   ack             = PKT-LINE("ACK" SP obj-id)
   nak             = PKT-LINE("NAK")
+  error-line     =  PKT-LINE("ERR" SP explanation-text)
 ----
 
 A simple clone may look like this (with no 'have' lines):
index 817d1cf7ef2a2a99ab11e5a88a27dfea673fec79..e681ede9d8b825569cfe2b39ec02fddca3a8fe40 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.12.GIT
+DEF_VER=v2.13.0-rc0
 
 LF='
 '
index ba524f3a7819db3ce2347cd76781b99241539735..eb1a1a7cffd5efc06ada671ca482eb638cd25d61 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -140,6 +140,13 @@ all::
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
+# Define DC_SHA1 to unconditionally enable the collision-detecting sha1
+# algorithm. This is slower, but may detect attempted collision attacks.
+# Takes priority over other *_SHA1 knobs.
+#
+# Define OPENSSL_SHA1 environment variable when running make to link
+# with the SHA1 routine from openssl library.
+#
 # Define SHA1_MAX_BLOCK_SIZE to limit the amount of data that will be hashed
 # in one call to the platform's SHA1_Update(). e.g. APPLE_COMMON_CRYPTO
 # wants 'SHA1_MAX_BLOCK_SIZE=1024L*1024L*1024L' defined.
@@ -614,14 +621,17 @@ TEST_PROGRAMS_NEED_X += test-fake-ssh
 TEST_PROGRAMS_NEED_X += test-genrandom
 TEST_PROGRAMS_NEED_X += test-hashmap
 TEST_PROGRAMS_NEED_X += test-index-version
+TEST_PROGRAMS_NEED_X += test-lazy-init-name-hash
 TEST_PROGRAMS_NEED_X += test-line-buffer
 TEST_PROGRAMS_NEED_X += test-match-trees
 TEST_PROGRAMS_NEED_X += test-mergesort
 TEST_PROGRAMS_NEED_X += test-mktemp
+TEST_PROGRAMS_NEED_X += test-online-cpus
 TEST_PROGRAMS_NEED_X += test-parse-options
 TEST_PROGRAMS_NEED_X += test-path-utils
 TEST_PROGRAMS_NEED_X += test-prio-queue
 TEST_PROGRAMS_NEED_X += test-read-cache
+TEST_PROGRAMS_NEED_X += test-ref-store
 TEST_PROGRAMS_NEED_X += test-regex
 TEST_PROGRAMS_NEED_X += test-revision-walking
 TEST_PROGRAMS_NEED_X += test-run-command
@@ -1383,20 +1393,27 @@ ifdef APPLE_COMMON_CRYPTO
        SHA1_MAX_BLOCK_SIZE = 1024L*1024L*1024L
 endif
 
+ifdef OPENSSL_SHA1
+       EXTLIBS += $(LIB_4_CRYPTO)
+       BASIC_CFLAGS += -DSHA1_OPENSSL
+else
 ifdef BLK_SHA1
-       SHA1_HEADER = "block-sha1/sha1.h"
        LIB_OBJS += block-sha1/sha1.o
+       BASIC_CFLAGS += -DSHA1_BLK
 else
 ifdef PPC_SHA1
-       SHA1_HEADER = "ppc/sha1.h"
        LIB_OBJS += ppc/sha1.o ppc/sha1ppc.o
+       BASIC_CFLAGS += -DSHA1_PPC
 else
 ifdef APPLE_COMMON_CRYPTO
        COMPAT_CFLAGS += -DCOMMON_DIGEST_FOR_OPENSSL
-       SHA1_HEADER = <CommonCrypto/CommonDigest.h>
+       BASIC_CFLAGS += -DSHA1_APPLE
 else
-       SHA1_HEADER = <openssl/sha.h>
-       EXTLIBS += $(LIB_4_CRYPTO)
+       DC_SHA1 := YesPlease
+       LIB_OBJS += sha1dc/sha1.o
+       LIB_OBJS += sha1dc/ubc_check.o
+       BASIC_CFLAGS += -DSHA1_DC
+endif
 endif
 endif
 endif
@@ -1588,7 +1605,6 @@ endif
 
 # Shell quote (do not use $(call) to accommodate ancient setups);
 
-SHA1_HEADER_SQ = $(subst ','\'',$(SHA1_HEADER))
 ETC_GITCONFIG_SQ = $(subst ','\'',$(ETC_GITCONFIG))
 ETC_GITATTRIBUTES_SQ = $(subst ','\'',$(ETC_GITATTRIBUTES))
 
@@ -1621,8 +1637,7 @@ PERLLIB_EXTRA_SQ = $(subst ','\'',$(PERLLIB_EXTRA))
 # from the dependency list, that would make each entry appear twice.
 LIBS = $(filter-out %.o, $(GITLIBS)) $(EXTLIBS)
 
-BASIC_CFLAGS += -DSHA1_HEADER='$(SHA1_HEADER_SQ)' \
-       $(COMPAT_CFLAGS)
+BASIC_CFLAGS += $(COMPAT_CFLAGS)
 LIB_OBJS += $(COMPAT_OBJS)
 
 # Quote for C
@@ -1838,6 +1853,7 @@ perl/perl.mak: perl/PM.stamp
 
 perl/PM.stamp: FORCE
        @$(FIND) perl -type f -name '*.pm' | sort >$@+ && \
+       $(PERL_PATH) -V >>$@+ && \
        { cmp $@+ $@ >/dev/null 2>/dev/null || mv $@+ $@; } && \
        $(RM) $@+
 
@@ -2225,6 +2241,7 @@ GIT-BUILD-OPTIONS: FORCE
        @echo NO_PYTHON=\''$(subst ','\'',$(subst ','\'',$(NO_PYTHON)))'\' >>$@+
        @echo NO_UNIX_SOCKETS=\''$(subst ','\'',$(subst ','\'',$(NO_UNIX_SOCKETS)))'\' >>$@+
        @echo PAGER_ENV=\''$(subst ','\'',$(subst ','\'',$(PAGER_ENV)))'\' >>$@+
+       @echo DC_SHA1=\''$(subst ','\'',$(subst ','\'',$(DC_SHA1)))'\' >>$@+
 ifdef TEST_OUTPUT_DIRECTORY
        @echo TEST_OUTPUT_DIRECTORY=\''$(subst ','\'',$(subst ','\'',$(TEST_OUTPUT_DIRECTORY)))'\' >>$@+
 endif
@@ -2334,9 +2351,17 @@ check: common-cmds.h
 C_SOURCES = $(patsubst %.o,%.c,$(C_OBJ))
 %.cocci.patch: %.cocci $(C_SOURCES)
        @echo '    ' SPATCH $<; \
+       ret=0; \
        for f in $(C_SOURCES); do \
-               $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS); \
-       done >$@ 2>$@.log; \
+               $(SPATCH) --sp-file $< $$f $(SPATCH_FLAGS) || \
+                       { ret=$$?; break; }; \
+       done >$@+ 2>$@.log; \
+       if test $$ret != 0; \
+       then \
+               cat $@.log; \
+               exit 1; \
+       fi; \
+       mv $@+ $@; \
        if test -s $@; \
        then \
                echo '    ' SPATCH result: $@; \
index b02e068aa347db683b00246061b48bfafabbc1c0..7f1cfe97929ea33efb81adfcc97b82f334dccb3d 100644 (file)
--- a/abspath.c
+++ b/abspath.c
@@ -246,29 +246,21 @@ char *absolute_pathdup(const char *path)
        return strbuf_detach(&sb, NULL);
 }
 
-/*
- * Unlike prefix_path, this should be used if the named file does
- * not have to interact with index entry; i.e. name of a random file
- * on the filesystem.
- */
-const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
+char *prefix_filename(const char *pfx, const char *arg)
 {
-       static struct strbuf path = STRBUF_INIT;
-#ifndef GIT_WINDOWS_NATIVE
-       if (!pfx_len || is_absolute_path(arg))
-               return arg;
-       strbuf_reset(&path);
-       strbuf_add(&path, pfx, pfx_len);
-       strbuf_addstr(&path, arg);
-#else
-       /* don't add prefix to absolute paths, but still replace '\' by '/' */
-       strbuf_reset(&path);
-       if (is_absolute_path(arg))
+       struct strbuf path = STRBUF_INIT;
+       size_t pfx_len = pfx ? strlen(pfx) : 0;
+
+       if (!pfx_len)
+               ; /* nothing to prefix */
+       else if (is_absolute_path(arg))
                pfx_len = 0;
-       else if (pfx_len)
+       else
                strbuf_add(&path, pfx, pfx_len);
+
        strbuf_addstr(&path, arg);
+#ifdef GIT_WINDOWS_NATIVE
        convert_slashes(path.buf + pfx_len);
 #endif
-       return path.buf;
+       return strbuf_detach(&path, NULL);
 }
diff --git a/apply.c b/apply.c
index 0e2caeab9cc523364e0cfbb8cbbb5aba1a0a58ee..e6dbab26ad54b9a3af06f6adad928c6e1526db58 100644 (file)
--- a/apply.c
+++ b/apply.c
@@ -2046,7 +2046,7 @@ static void prefix_one(struct apply_state *state, char **name)
        char *old_name = *name;
        if (!old_name)
                return;
-       *name = xstrdup(prefix_filename(state->prefix, state->prefix_length, *name));
+       *name = prefix_filename(state->prefix, *name);
        free(old_name);
 }
 
@@ -4805,6 +4805,7 @@ int apply_all_patches(struct apply_state *state,
 
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
+               char *to_free = NULL;
                int fd;
 
                if (!strcmp(arg, "-")) {
@@ -4814,21 +4815,21 @@ int apply_all_patches(struct apply_state *state,
                        errs |= res;
                        read_stdin = 0;
                        continue;
-               } else if (0 < state->prefix_length)
-                       arg = prefix_filename(state->prefix,
-                                             state->prefix_length,
-                                             arg);
+               } else
+                       arg = to_free = prefix_filename(state->prefix, arg);
 
                fd = open(arg, O_RDONLY);
                if (fd < 0) {
                        error(_("can't open patch '%s': %s"), arg, strerror(errno));
                        res = -128;
+                       free(to_free);
                        goto end;
                }
                read_stdin = 0;
                set_default_whitespace_mode(state);
                res = apply_patch(state, fd, arg, options);
                close(fd);
+               free(to_free);
                if (res < 0)
                        goto end;
                errs |= res;
index 30808cadf7613d73cae2e4caaedd922ba9a48ca0..03af06c66cebcbc0699b8a074f9ef120da09248a 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -12,8 +12,8 @@
 #include "sha1-array.h"
 #include "argv-array.h"
 
-static struct sha1_array good_revs;
-static struct sha1_array skipped_revs;
+static struct oid_array good_revs;
+static struct oid_array skipped_revs;
 
 static struct object_id *current_bad_oid;
 
@@ -200,6 +200,7 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
 {
        struct commit_list *p;
        struct commit_dist *array = xcalloc(nr, sizeof(*array));
+       struct strbuf buf = STRBUF_INIT;
        int cnt, i;
 
        for (p = list, cnt = 0; p; p = p->next) {
@@ -217,17 +218,18 @@ static struct commit_list *best_bisection_sorted(struct commit_list *list, int n
        }
        QSORT(array, cnt, compare_commit_dist);
        for (p = list, i = 0; i < cnt; i++) {
-               char buf[100]; /* enough for dist=%d */
                struct object *obj = &(array[i].commit->object);
 
-               snprintf(buf, sizeof(buf), "dist=%d", array[i].distance);
-               add_name_decoration(DECORATION_NONE, buf, obj);
+               strbuf_reset(&buf);
+               strbuf_addf(&buf, "dist=%d", array[i].distance);
+               add_name_decoration(DECORATION_NONE, buf.buf, obj);
 
                p->item = array[i].commit;
                p = p->next;
        }
        if (p)
                p->next = NULL;
+       strbuf_release(&buf);
        free(array);
        return list;
 }
@@ -413,9 +415,9 @@ static int register_ref(const char *refname, const struct object_id *oid,
                current_bad_oid = xmalloc(sizeof(*current_bad_oid));
                oidcpy(current_bad_oid, oid);
        } else if (starts_with(refname, good_prefix.buf)) {
-               sha1_array_append(&good_revs, oid->hash);
+               oid_array_append(&good_revs, oid);
        } else if (starts_with(refname, "skip-")) {
-               sha1_array_append(&skipped_revs, oid->hash);
+               oid_array_append(&skipped_revs, oid);
        }
 
        strbuf_release(&good_prefix);
@@ -451,13 +453,13 @@ static void read_bisect_paths(struct argv_array *array)
        fclose(fp);
 }
 
-static char *join_sha1_array_hex(struct sha1_array *array, char delim)
+static char *join_sha1_array_hex(struct oid_array *array, char delim)
 {
        struct strbuf joined_hexs = STRBUF_INIT;
        int i;
 
        for (i = 0; i < array->nr; i++) {
-               strbuf_addstr(&joined_hexs, sha1_to_hex(array->sha1[i]));
+               strbuf_addstr(&joined_hexs, oid_to_hex(array->oid + i));
                if (i + 1 < array->nr)
                        strbuf_addch(&joined_hexs, delim);
        }
@@ -499,8 +501,7 @@ struct commit_list *filter_skipped(struct commit_list *list,
        while (list) {
                struct commit_list *next = list->next;
                list->next = NULL;
-               if (0 <= sha1_array_lookup(&skipped_revs,
-                                          list->item->object.oid.hash)) {
+               if (0 <= oid_array_lookup(&skipped_revs, &list->item->object.oid)) {
                        if (skipped_first && !*skipped_first)
                                *skipped_first = 1;
                        /* Move current to tried list */
@@ -621,7 +622,7 @@ static void bisect_rev_setup(struct rev_info *revs, const char *prefix,
        argv_array_pushf(&rev_argv, bad_format, oid_to_hex(current_bad_oid));
        for (i = 0; i < good_revs.nr; i++)
                argv_array_pushf(&rev_argv, good_format,
-                                sha1_to_hex(good_revs.sha1[i]));
+                                oid_to_hex(good_revs.oid + i));
        argv_array_push(&rev_argv, "--");
        if (read_paths)
                read_bisect_paths(&rev_argv);
@@ -682,7 +683,7 @@ static int is_expected_rev(const struct object_id *oid)
 
 static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
 {
-       char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
+       char bisect_rev_hex[GIT_MAX_HEXSZ + 1];
 
        memcpy(bisect_rev_hex, sha1_to_hex(bisect_rev), GIT_SHA1_HEXSZ + 1);
        update_ref(NULL, "BISECT_EXPECTED_REV", bisect_rev, NULL, 0, UPDATE_REFS_DIE_ON_ERR);
@@ -701,11 +702,11 @@ static int bisect_checkout(const unsigned char *bisect_rev, int no_checkout)
        return run_command_v_opt(argv_show_branch, RUN_GIT_CMD);
 }
 
-static struct commit *get_commit_reference(const unsigned char *sha1)
+static struct commit *get_commit_reference(const struct object_id *oid)
 {
-       struct commit *r = lookup_commit_reference(sha1);
+       struct commit *r = lookup_commit_reference(oid->hash);
        if (!r)
-               die(_("Not a valid commit name %s"), sha1_to_hex(sha1));
+               die(_("Not a valid commit name %s"), oid_to_hex(oid));
        return r;
 }
 
@@ -715,9 +716,9 @@ static struct commit **get_bad_and_good_commits(int *rev_nr)
        int i, n = 0;
 
        ALLOC_ARRAY(rev, 1 + good_revs.nr);
-       rev[n++] = get_commit_reference(current_bad_oid->hash);
+       rev[n++] = get_commit_reference(current_bad_oid);
        for (i = 0; i < good_revs.nr; i++)
-               rev[n++] = get_commit_reference(good_revs.sha1[i]);
+               rev[n++] = get_commit_reference(good_revs.oid + i);
        *rev_nr = n;
 
        return rev;
@@ -754,9 +755,9 @@ static void handle_bad_merge_base(void)
        exit(1);
 }
 
-static void handle_skipped_merge_base(const unsigned char *mb)
+static void handle_skipped_merge_base(const struct object_id *mb)
 {
-       char *mb_hex = sha1_to_hex(mb);
+       char *mb_hex = oid_to_hex(mb);
        char *bad_hex = oid_to_hex(current_bad_oid);
        char *good_hex = join_sha1_array_hex(&good_revs, ' ');
 
@@ -787,16 +788,16 @@ static void check_merge_bases(int no_checkout)
        result = get_merge_bases_many(rev[0], rev_nr - 1, rev + 1);
 
        for (; result; result = result->next) {
-               const unsigned char *mb = result->item->object.oid.hash;
-               if (!hashcmp(mb, current_bad_oid->hash)) {
+               const struct object_id *mb = &result->item->object.oid;
+               if (!oidcmp(mb, current_bad_oid)) {
                        handle_bad_merge_base();
-               } else if (0 <= sha1_array_lookup(&good_revs, mb)) {
+               } else if (0 <= oid_array_lookup(&good_revs, mb)) {
                        continue;
-               } else if (0 <= sha1_array_lookup(&skipped_revs, mb)) {
+               } else if (0 <= oid_array_lookup(&skipped_revs, mb)) {
                        handle_skipped_merge_base(mb);
                } else {
                        printf(_("Bisecting: a merge base must be tested\n"));
-                       exit(bisect_checkout(mb, no_checkout));
+                       exit(bisect_checkout(mb->hash, no_checkout));
                }
        }
 
index 5c12036b0206fe250f50bea30578785b090f9ee4..ad5a2299ba2600e67c8aaa7719a2c7d54898958f 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -234,7 +234,7 @@ void create_branch(const char *name, const char *start_name,
 {
        struct commit *commit;
        unsigned char sha1[20];
-       char *real_ref, msg[PATH_MAX + 20];
+       char *real_ref;
        struct strbuf ref = STRBUF_INIT;
        int forcing = 0;
        int dont_change_ref = 0;
@@ -290,19 +290,18 @@ void create_branch(const char *name, const char *start_name,
                die(_("Not a valid branch point: '%s'."), start_name);
        hashcpy(sha1, commit->object.oid.hash);
 
-       if (forcing)
-               snprintf(msg, sizeof msg, "branch: Reset to %s",
-                        start_name);
-       else if (!dont_change_ref)
-               snprintf(msg, sizeof msg, "branch: Created from %s",
-                        start_name);
-
        if (reflog)
                log_all_ref_updates = LOG_REFS_NORMAL;
 
        if (!dont_change_ref) {
                struct ref_transaction *transaction;
                struct strbuf err = STRBUF_INIT;
+               char *msg;
+
+               if (forcing)
+                       msg = xstrfmt("branch: Reset to %s", start_name);
+               else
+                       msg = xstrfmt("branch: Created from %s", start_name);
 
                transaction = ref_transaction_begin(&err);
                if (!transaction ||
@@ -313,6 +312,7 @@ void create_branch(const char *name, const char *start_name,
                        die("%s", err.buf);
                ref_transaction_free(transaction);
                strbuf_release(&err);
+               free(msg);
        }
 
        if (real_ref && track)
index f7a7a971fbe762a4fb691e786e86f681f2bbd7c1..f08b7e6626021e425e4712763bee2236b8eab47b 100644 (file)
@@ -762,14 +762,18 @@ static int split_mail_conv(mail_conv_fn fn, struct am_state *state,
                mail = mkpath("%s/%0*d", state->dir, state->prec, i + 1);
 
                out = fopen(mail, "w");
-               if (!out)
+               if (!out) {
+                       if (in != stdin)
+                               fclose(in);
                        return error_errno(_("could not open '%s' for writing"),
                                           mail);
+               }
 
                ret = fn(out, in, keep_cr);
 
                fclose(out);
-               fclose(in);
+               if (in != stdin)
+                       fclose(in);
 
                if (ret)
                        return error(_("could not parse patch '%s'"), *paths);
@@ -1181,42 +1185,39 @@ static void NORETURN die_user_resolve(const struct am_state *state)
        exit(128);
 }
 
-static void am_signoff(struct strbuf *sb)
+/**
+ * Appends signoff to the "msg" field of the am_state.
+ */
+static void am_append_signoff(struct am_state *state)
 {
        char *cp;
        struct strbuf mine = STRBUF_INIT;
+       struct strbuf sb = STRBUF_INIT;
 
-       /* Does it end with our own sign-off? */
+       strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
+
+       /* our sign-off */
        strbuf_addf(&mine, "\n%s%s\n",
                    sign_off_header,
                    fmt_name(getenv("GIT_COMMITTER_NAME"),
                             getenv("GIT_COMMITTER_EMAIL")));
-       if (mine.len < sb->len &&
-           !strcmp(mine.buf, sb->buf + sb->len - mine.len))
+
+       /* Does sb end with it already? */
+       if (mine.len < sb.len &&
+           !strcmp(mine.buf, sb.buf + sb.len - mine.len))
                goto exit; /* no need to duplicate */
 
        /* Does it have any Signed-off-by: in the text */
-       for (cp = sb->buf;
+       for (cp = sb.buf;
             cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
             cp = strchr(cp, '\n')) {
-               if (sb->buf == cp || cp[-1] == '\n')
+               if (sb.buf == cp || cp[-1] == '\n')
                        break;
        }
 
-       strbuf_addstr(sb, mine.buf + !!cp);
+       strbuf_addstr(&sb, mine.buf + !!cp);
 exit:
        strbuf_release(&mine);
-}
-
-/**
- * Appends signoff to the "msg" field of the am_state.
- */
-static void am_append_signoff(struct am_state *state)
-{
-       struct strbuf sb = STRBUF_INIT;
-
-       strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
-       am_signoff(&sb);
        state->msg = strbuf_detach(&sb, &state->msg_len);
 }
 
@@ -1321,9 +1322,6 @@ static int parse_mail(struct am_state *state, const char *mail)
        strbuf_addbuf(&msg, &mi.log_message);
        strbuf_stripspace(&msg, 0);
 
-       if (state->signoff)
-               am_signoff(&msg);
-
        assert(!state->author_name);
        state->author_name = strbuf_detach(&author_name, NULL);
 
@@ -1848,6 +1846,9 @@ static void am_run(struct am_state *state, int resume)
                        if (skip)
                                goto next; /* mail should be skipped */
 
+                       if (state->signoff)
+                               am_append_signoff(state);
+
                        write_author_script(state);
                        write_commit_msg(state);
                }
index f7aa95f4babaab904443784eccdaa3b219bd2b08..07506a3e457d1540acb7700e4677d715d6842735 100644 (file)
@@ -1890,7 +1890,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
        int cnt;
        const char *cp;
        struct origin *suspect = ent->suspect;
-       char hex[GIT_SHA1_HEXSZ + 1];
+       char hex[GIT_MAX_HEXSZ + 1];
 
        oid_to_hex_r(hex, &suspect->commit->object.oid);
        printf("%s %d %d %d\n",
@@ -1928,7 +1928,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
        const char *cp;
        struct origin *suspect = ent->suspect;
        struct commit_info ci;
-       char hex[GIT_SHA1_HEXSZ + 1];
+       char hex[GIT_MAX_HEXSZ + 1];
        int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
 
        get_commit_info(suspect->commit, &ci, 1);
index 52688f2e1be91024ba6cb170b4b38b393e8ab8ba..0552c42ad115bba218f35d4836f1021e3385f6c2 100644 (file)
@@ -562,7 +562,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                OPT_SET_INT('r', "remotes",     &filter.kind, N_("act on remote-tracking branches"),
                        FILTER_REFS_REMOTES),
                OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")),
+               OPT_NO_CONTAINS(&filter.no_commit, N_("print only branches that don't contain the commit")),
                OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")),
+               OPT_WITHOUT(&filter.no_commit, N_("print only branches that don't contain the commit")),
                OPT__ABBREV(&filter.abbrev),
 
                OPT_GROUP(N_("Specific git-branch actions:")),
@@ -618,7 +620,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0)
                list = 1;
 
-       if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr)
+       if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr ||
+           filter.no_commit)
                list = 1;
 
        if (!!delete + !!rename + !!new_upstream +
index 4883a435a9afc607618d34c9b61b218eeda7de45..d0de59b94ff23eebe03984dc670babf793bdd5ff 100644 (file)
@@ -20,21 +20,15 @@ int cmd_bundle(int argc, const char **argv, const char *prefix)
        struct bundle_header header;
        const char *cmd, *bundle_file;
        int bundle_fd = -1;
-       char buffer[PATH_MAX];
 
        if (argc < 3)
                usage(builtin_bundle_usage);
 
        cmd = argv[1];
-       bundle_file = argv[2];
+       bundle_file = prefix_filename(prefix, argv[2]);
        argc -= 2;
        argv += 2;
 
-       if (prefix && bundle_file[0] != '/') {
-               snprintf(buffer, sizeof(buffer), "%s/%s", prefix, bundle_file);
-               bundle_file = buffer;
-       }
-
        memset(&header, 0, sizeof(header));
        if (strcmp(cmd, "create") && (bundle_fd =
                                read_bundle_header(bundle_file, &header)) < 0)
index 8b85cb8cf083e70580f77392cd5e6391318d19c6..1890d7a6390dcae719af33ce5cb59e74832a80e8 100644 (file)
@@ -401,10 +401,10 @@ struct object_cb_data {
        struct expand_data *expand;
 };
 
-static int batch_object_cb(const unsigned char sha1[20], void *vdata)
+static int batch_object_cb(const struct object_id *oid, void *vdata)
 {
        struct object_cb_data *data = vdata;
-       hashcpy(data->expand->oid.hash, sha1);
+       oidcpy(&data->expand->oid, oid);
        batch_object_write(NULL, data->opt, data->expand);
        return 0;
 }
@@ -413,7 +413,7 @@ static int batch_loose_object(const struct object_id *oid,
                              const char *path,
                              void *data)
 {
-       sha1_array_append(data, oid->hash);
+       oid_array_append(data, oid);
        return 0;
 }
 
@@ -422,7 +422,7 @@ static int batch_packed_object(const struct object_id *oid,
                               uint32_t pos,
                               void *data)
 {
-       sha1_array_append(data, oid->hash);
+       oid_array_append(data, oid);
        return 0;
 }
 
@@ -462,7 +462,7 @@ static int batch_objects(struct batch_options *opt)
                data.info.typep = &data.type;
 
        if (opt->all_objects) {
-               struct sha1_array sa = SHA1_ARRAY_INIT;
+               struct oid_array sa = OID_ARRAY_INIT;
                struct object_cb_data cb;
 
                for_each_loose_object(batch_loose_object, &sa, 0);
@@ -470,9 +470,9 @@ static int batch_objects(struct batch_options *opt)
 
                cb.opt = opt;
                cb.expand = &data;
-               sha1_array_for_each_unique(&sa, batch_object_cb, &cb);
+               oid_array_for_each_unique(&sa, batch_object_cb, &cb);
 
-               sha1_array_clear(&sa);
+               oid_array_clear(&sa);
                return 0;
        }
 
index 81f07c3ef271db08e36ee7a89b1d128d099ecb96..bfa5419f335dc1db98059869de24f152cffb9aa6 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;
@@ -889,11 +908,10 @@ static int check_tracking_name(struct remote *remote, void *cb_data)
 static const char *unique_tracking_name(const char *name, struct object_id *oid)
 {
        struct tracking_name_data cb_data = { NULL, NULL, NULL, 1 };
-       char src_ref[PATH_MAX];
-       snprintf(src_ref, PATH_MAX, "refs/heads/%s", name);
-       cb_data.src_ref = src_ref;
+       cb_data.src_ref = xstrfmt("refs/heads/%s", name);
        cb_data.dst_oid = oid;
        for_each_remote(check_tracking_name, &cb_data);
+       free(cb_data.src_ref);
        if (cb_data.unique)
                return cb_data.dst_ref;
        free(cb_data.dst_ref);
@@ -1163,6 +1181,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,
+                           "checkout", "control recursive updating of submodules",
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules },
                OPT_BOOL(0, "progress", &opts.show_progress, N_("force progress reporting")),
                OPT_END(),
        };
@@ -1193,6 +1214,12 @@ 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"));
 
index b4c929bb8afbf94967949279a1cc3b9b21dae100..de85b85254e49ba0211ea6476179fc6d4c774ca9 100644 (file)
@@ -39,7 +39,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, option_recursive;
+static int option_local = -1, option_no_hardlinks, option_shared;
 static int option_shallow_submodules;
 static int deepen;
 static char *option_template, *option_depth, *option_since;
@@ -56,6 +56,21 @@ static struct string_list option_required_reference = STRING_LIST_INIT_NODUP;
 static struct string_list option_optional_reference = STRING_LIST_INIT_NODUP;
 static int option_dissociate;
 static int max_jobs = -1;
+static struct string_list option_recurse_submodules = STRING_LIST_INIT_NODUP;
+
+static int recurse_submodules_cb(const struct option *opt,
+                                const char *arg, int unset)
+{
+       if (unset)
+               string_list_clear((struct string_list *)opt->value, 0);
+       else if (arg)
+               string_list_append((struct string_list *)opt->value, arg);
+       else
+               string_list_append((struct string_list *)opt->value,
+                                  (const char *)opt->defval);
+
+       return 0;
+}
 
 static struct option builtin_clone_options[] = {
        OPT__VERBOSITY(&option_verbosity),
@@ -74,10 +89,13 @@ static struct option builtin_clone_options[] = {
                    N_("don't use local hardlinks, always copy")),
        OPT_BOOL('s', "shared", &option_shared,
                    N_("setup as shared repository")),
-       OPT_BOOL(0, "recursive", &option_recursive,
-                   N_("initialize submodules in the clone")),
-       OPT_BOOL(0, "recurse-submodules", &option_recursive,
-                   N_("initialize submodules in the clone")),
+       { OPTION_CALLBACK, 0, "recursive", &option_recurse_submodules,
+         N_("pathspec"), N_("initialize submodules in the clone"),
+         PARSE_OPT_OPTARG | PARSE_OPT_HIDDEN, recurse_submodules_cb,
+         (intptr_t)"." },
+       { OPTION_CALLBACK, 0, "recurse-submodules", &option_recurse_submodules,
+         N_("pathspec"), N_("initialize submodules in the clone"),
+         PARSE_OPT_OPTARG, recurse_submodules_cb, (intptr_t)"." },
        OPT_INTEGER('j', "jobs", &max_jobs,
                    N_("number of submodules cloned in parallel")),
        OPT_STRING(0, "template", &option_template, N_("template-directory"),
@@ -733,7 +751,7 @@ static int checkout(int submodule_progress)
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
                           oid_to_hex(&oid), "1", NULL);
 
-       if (!err && option_recursive) {
+       if (!err && (option_recurse_submodules.nr > 0)) {
                struct argv_array args = ARGV_ARRAY_INIT;
                argv_array_pushl(&args, "submodule", "update", "--init", "--recursive", NULL);
 
@@ -957,7 +975,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                        fprintf(stderr, _("Cloning into '%s'...\n"), dir);
        }
 
-       if (option_recursive) {
+       if (option_recurse_submodules.nr > 0) {
+               struct string_list_item *item;
+               struct strbuf sb = STRBUF_INIT;
+
+               /* remove duplicates */
+               string_list_sort(&option_recurse_submodules);
+               string_list_remove_duplicates(&option_recurse_submodules, 0);
+
+               /*
+                * NEEDSWORK: In a multi-working-tree world, this needs to be
+                * set in the per-worktree config.
+                */
+               for_each_string_list_item(item, &option_recurse_submodules) {
+                       strbuf_addf(&sb, "submodule.active=%s",
+                                   item->string);
+                       string_list_append(&option_config,
+                                          strbuf_detach(&sb, NULL));
+               }
+
                if (option_required_reference.nr &&
                    option_optional_reference.nr)
                        die(_("clone --recursive is not compatible with "
index 4e288bc5132dbc3846382c29b89a15d7bdf7c211..ad188fea9ec552c65c4b82c3558977dd6648fe77 100644 (file)
@@ -1404,7 +1404,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
 static const char *implicit_ident_advice(void)
 {
-       char *user_config = expand_user_path("~/.gitconfig");
+       char *user_config = expand_user_path("~/.gitconfig", 0);
        char *xdg_config = xdg_config_home("config");
        int config_exists = file_exists(user_config) || file_exists(xdg_config);
 
index 05843a0f96e4dc0dbf9fbc7310039794b57947e7..3f7c8763d21764a43749163ef26d7c3c30877bf3 100644 (file)
@@ -502,7 +502,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        }
 
        if (use_global_config) {
-               char *user_config = expand_user_path("~/.gitconfig");
+               char *user_config = expand_user_path("~/.gitconfig", 0);
                char *xdg_config = xdg_config_home("config");
 
                if (!user_config)
@@ -527,9 +527,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
        else if (given_config_source.file) {
                if (!is_absolute_path(given_config_source.file) && prefix)
                        given_config_source.file =
-                               xstrdup(prefix_filename(prefix,
-                                                       strlen(prefix),
-                                                       given_config_source.file));
+                               prefix_filename(prefix, given_config_source.file);
        }
 
        if (respect_includes == -1)
index 76c18059bf7ad059d4cca70712f4a782cc02d0eb..a5cd8c513f96baaaa0d9a310e2bf02b8db41199b 100644 (file)
@@ -9,6 +9,7 @@
 #include "diff.h"
 #include "hashmap.h"
 #include "argv-array.h"
+#include "run-command.h"
 
 #define SEEN           (1u << 0)
 #define MAX_TAGS       (FLAG_BITS - 1)
@@ -31,7 +32,7 @@ static int have_util;
 static struct string_list patterns = STRING_LIST_INIT_NODUP;
 static struct string_list exclude_patterns = STRING_LIST_INIT_NODUP;
 static int always;
-static const char *dirty;
+static const char *suffix, *dirty, *broken;
 
 /* diff-index command arguments to check if working tree is dirty. */
 static const char *diff_index_args[] = {
@@ -49,7 +50,7 @@ struct commit_name {
 };
 
 static const char *prio_names[] = {
-       "head", "lightweight", "annotated",
+       N_("head"), N_("lightweight"), N_("annotated"),
 };
 
 static int commit_name_cmp(const struct commit_name *cn1,
@@ -292,8 +293,8 @@ static void describe(const char *arg, int last_one)
                display_name(n);
                if (longformat)
                        show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
-               if (dirty)
-                       printf("%s", dirty);
+               if (suffix)
+                       printf("%s", suffix);
                printf("\n");
                return;
        }
@@ -369,8 +370,8 @@ static void describe(const char *arg, int last_one)
                struct object_id *oid = &cmit->object.oid;
                if (always) {
                        printf("%s", find_unique_abbrev(oid->hash, abbrev));
-                       if (dirty)
-                               printf("%s", dirty);
+                       if (suffix)
+                               printf("%s", suffix);
                        printf("\n");
                        return;
                }
@@ -394,10 +395,19 @@ static void describe(const char *arg, int last_one)
        free_commit_list(list);
 
        if (debug) {
+               static int label_width = -1;
+               if (label_width < 0) {
+                       int i, w;
+                       for (i = 0; i < ARRAY_SIZE(prio_names); i++) {
+                               w = strlen(_(prio_names[i]));
+                               if (label_width < w)
+                                       label_width = w;
+                       }
+               }
                for (cur_match = 0; cur_match < match_cnt; cur_match++) {
                        struct possible_tag *t = &all_matches[cur_match];
-                       fprintf(stderr, " %-11s %8d %s\n",
-                               prio_names[t->name->prio],
+                       fprintf(stderr, " %-*s %8d %s\n",
+                               label_width, _(prio_names[t->name->prio]),
                                t->depth, t->name->path);
                }
                fprintf(stderr, _("traversed %lu commits\n"), seen_commits);
@@ -413,8 +423,8 @@ static void describe(const char *arg, int last_one)
        display_name(all_matches[0].name);
        if (abbrev)
                show_suffix(all_matches[0].depth, &cmit->object.oid);
-       if (dirty)
-               printf("%s", dirty);
+       if (suffix)
+               printf("%s", suffix);
        printf("\n");
 
        if (!last_one)
@@ -445,6 +455,9 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                {OPTION_STRING, 0, "dirty",  &dirty, N_("mark"),
                        N_("append <mark> on dirty working tree (default: \"-dirty\")"),
                        PARSE_OPT_OPTARG, NULL, (intptr_t) "-dirty"},
+               {OPTION_STRING, 0, "broken",  &broken, N_("mark"),
+                       N_("append <mark> on broken working tree (default: \"-broken\")"),
+                       PARSE_OPT_OPTARG, NULL, (intptr_t) "-broken"},
                OPT_END(),
        };
 
@@ -493,7 +506,28 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                die(_("No names found, cannot describe anything."));
 
        if (argc == 0) {
-               if (dirty) {
+               if (broken) {
+                       struct child_process cp = CHILD_PROCESS_INIT;
+                       argv_array_pushv(&cp.args, diff_index_args);
+                       cp.git_cmd = 1;
+                       cp.no_stdin = 1;
+                       cp.no_stdout = 1;
+
+                       if (!dirty)
+                               dirty = "-dirty";
+
+                       switch (run_command(&cp)) {
+                       case 0:
+                               suffix = NULL;
+                               break;
+                       case 1:
+                               suffix = dirty;
+                               break;
+                       default:
+                               /* diff-index aborted abnormally */
+                               suffix = broken;
+                       }
+               } else if (dirty) {
                        static struct lock_file index_lock;
                        int fd;
 
@@ -506,11 +540,15 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 
                        if (!cmd_diff_index(ARRAY_SIZE(diff_index_args) - 1,
                                            diff_index_args, prefix))
-                               dirty = NULL;
+                               suffix = NULL;
+                       else
+                               suffix = dirty;
                }
                describe("HEAD", 1);
        } else if (dirty) {
                die(_("--dirty is incompatible with commit-ishes"));
+       } else if (broken) {
+               die(_("--broken is incompatible with commit-ishes"));
        } else {
                while (argc-- > 0)
                        describe(*argv++, argc == 0);
index 3d64b8533732b5d4af971e6b1cd23ff66123e8e9..d184aafab9e353279125f9f7f07b63c3780bcba1 100644 (file)
@@ -21,7 +21,7 @@
 #define DIFF_NO_INDEX_IMPLICIT 2
 
 struct blobinfo {
-       unsigned char sha1[20];
+       struct object_id oid;
        const char *name;
        unsigned mode;
 };
@@ -31,22 +31,22 @@ static const char builtin_diff_usage[] =
 
 static void stuff_change(struct diff_options *opt,
                         unsigned old_mode, unsigned new_mode,
-                        const unsigned char *old_sha1,
-                        const unsigned char *new_sha1,
-                        int old_sha1_valid,
-                        int new_sha1_valid,
+                        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)
 {
        struct diff_filespec *one, *two;
 
-       if (!is_null_sha1(old_sha1) && !is_null_sha1(new_sha1) &&
-           !hashcmp(old_sha1, new_sha1) && (old_mode == new_mode))
+       if (!is_null_oid(old_oid) && !is_null_oid(new_oid) &&
+           !oidcmp(old_oid, new_oid) && (old_mode == new_mode))
                return;
 
        if (DIFF_OPT_TST(opt, REVERSE_DIFF)) {
                SWAP(old_mode, new_mode);
-               SWAP(old_sha1, new_sha1);
+               SWAP(old_oid, new_oid);
                SWAP(old_name, new_name);
        }
 
@@ -57,8 +57,8 @@ static void stuff_change(struct diff_options *opt,
 
        one = alloc_filespec(old_name);
        two = alloc_filespec(new_name);
-       fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
-       fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
+       fill_filespec(one, old_oid->hash, old_oid_valid, old_mode);
+       fill_filespec(two, new_oid->hash, new_oid_valid, new_mode);
 
        diff_queue(&diff_queued_diff, one, two);
 }
@@ -89,7 +89,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
 
        stuff_change(&revs->diffopt,
                     blob[0].mode, canon_mode(st.st_mode),
-                    blob[0].sha1, null_sha1,
+                    &blob[0].oid, &null_oid,
                     1, 0,
                     path, path);
        diffcore_std(&revs->diffopt);
@@ -114,7 +114,7 @@ static int builtin_diff_blobs(struct rev_info *revs,
 
        stuff_change(&revs->diffopt,
                     blob[0].mode, blob[1].mode,
-                    blob[0].sha1, blob[1].sha1,
+                    &blob[0].oid, &blob[1].oid,
                     1, 1,
                     blob[0].name, blob[1].name);
        diffcore_std(&revs->diffopt);
@@ -160,7 +160,7 @@ static int builtin_diff_tree(struct rev_info *revs,
                             struct object_array_entry *ent0,
                             struct object_array_entry *ent1)
 {
-       const unsigned char *(sha1[2]);
+       const struct object_id *(oid[2]);
        int swap = 0;
 
        if (argc > 1)
@@ -172,9 +172,9 @@ static int builtin_diff_tree(struct rev_info *revs,
         */
        if (ent1->item->flags & UNINTERESTING)
                swap = 1;
-       sha1[swap] = ent0->item->oid.hash;
-       sha1[1 - swap] = ent1->item->oid.hash;
-       diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
+       oid[swap] = &ent0->item->oid;
+       oid[1 - swap] = &ent1->item->oid;
+       diff_tree_sha1(oid[0]->hash, oid[1]->hash, "", &revs->diffopt);
        log_tree_diff_flush(revs);
        return 0;
 }
@@ -184,7 +184,7 @@ static int builtin_diff_combined(struct rev_info *revs,
                                 struct object_array_entry *ent,
                                 int ents)
 {
-       struct sha1_array parents = SHA1_ARRAY_INIT;
+       struct oid_array parents = OID_ARRAY_INIT;
        int i;
 
        if (argc > 1)
@@ -193,10 +193,10 @@ static int builtin_diff_combined(struct rev_info *revs,
        if (!revs->dense_combined_merges && !revs->combine_merges)
                revs->dense_combined_merges = revs->combine_merges = 1;
        for (i = 1; i < ents; i++)
-               sha1_array_append(&parents, ent[i].item->oid.hash);
+               oid_array_append(&parents, &ent[i].item->oid);
        diff_tree_combined(ent[0].item->oid.hash, &parents,
                           revs->dense_combined_merges, revs);
-       sha1_array_clear(&parents);
+       oid_array_clear(&parents);
        return 0;
 }
 
@@ -408,7 +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].sha1, obj->oid.hash);
+                       hashcpy(blob[blobs].oid.hash, obj->oid.hash);
                        blob[blobs].name = name;
                        blob[blobs].mode = entry->mode;
                        blobs++;
index d13350ce832cd8c0064c8a5b2d3c9be64f876b8d..1354d0e4625c855676e7812be92d7c77bdecb407 100644 (file)
@@ -254,6 +254,62 @@ static int ensure_leading_directories(char *path)
        }
 }
 
+/*
+ * Unconditional writing of a plain regular file is what
+ * "git difftool --dir-diff" wants to do for symlinks.  We are preparing two
+ * temporary directories to be fed to a Git-unaware tool that knows how to
+ * show a diff of two directories (e.g. "diff -r A B").
+ *
+ * Because the tool is Git-unaware, if a symbolic link appears in either of
+ * these temporary directories, it will try to dereference and show the
+ * difference of the target of the symbolic link, which is not what we want,
+ * as the goal of the dir-diff mode is to produce an output that is logically
+ * equivalent to what "git diff" produces.
+ *
+ * Most importantly, we want to get textual comparison of the result of the
+ * readlink(2).  get_symlink() provides that---it returns the contents of
+ * the symlink that gets written to a regular file to force the external tool
+ * to compare the readlink(2) result as text, even on a filesystem that is
+ * capable of doing a symbolic link.
+ */
+static char *get_symlink(const struct object_id *oid, const char *path)
+{
+       char *data;
+       if (is_null_oid(oid)) {
+               /* The symlink is unknown to Git so read from the filesystem */
+               struct strbuf link = STRBUF_INIT;
+               if (has_symlinks) {
+                       if (strbuf_readlink(&link, path, strlen(path)))
+                               die(_("could not read symlink %s"), path);
+               } else if (strbuf_read_file(&link, path, 128))
+                       die(_("could not read symlink file %s"), path);
+
+               data = strbuf_detach(&link, NULL);
+       } else {
+               enum object_type type;
+               unsigned long size;
+               data = read_sha1_file(oid->hash, &type, &size);
+               if (!data)
+                       die(_("could not read object %s for symlink %s"),
+                               oid_to_hex(oid), path);
+       }
+
+       return data;
+}
+
+static int checkout_path(unsigned mode, struct object_id *oid,
+                        const char *path, const struct checkout *state)
+{
+       struct cache_entry *ce;
+       int ret;
+
+       ce = make_cache_entry(mode, oid->hash, path, 0, 0);
+       ret = checkout_entry(ce, state, NULL);
+
+       free(ce);
+       return ret;
+}
+
 static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                        int argc, const char **argv)
 {
@@ -262,16 +318,14 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        struct strbuf rpath = STRBUF_INIT, buf = STRBUF_INIT;
        struct strbuf ldir = STRBUF_INIT, rdir = STRBUF_INIT;
        struct strbuf wtdir = STRBUF_INIT;
+       char *lbase_dir, *rbase_dir;
        size_t ldir_len, rdir_len, wtdir_len;
-       struct cache_entry *ce = xcalloc(1, sizeof(ce) + PATH_MAX + 1);
        const char *workdir, *tmp;
        int ret = 0, i;
        FILE *fp;
        struct hashmap working_tree_dups, submodules, symlinks2;
        struct hashmap_iter iter;
        struct pair_entry *entry;
-       enum object_type type;
-       unsigned long size;
        struct index_state wtindex;
        struct checkout lstate, rstate;
        int rc, flags = RUN_GIT_CMD, err = 0;
@@ -298,11 +352,11 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
        memset(&wtindex, 0, sizeof(wtindex));
 
        memset(&lstate, 0, sizeof(lstate));
-       lstate.base_dir = ldir.buf;
+       lstate.base_dir = lbase_dir = xstrdup(ldir.buf);
        lstate.base_dir_len = ldir.len;
        lstate.force = 1;
        memset(&rstate, 0, sizeof(rstate));
-       rstate.base_dir = rdir.buf;
+       rstate.base_dir = rbase_dir = xstrdup(rdir.buf);
        rstate.base_dir_len = rdir.len;
        rstate.force = 1;
 
@@ -336,7 +390,6 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                struct object_id loid, roid;
                char status;
                const char *src_path, *dst_path;
-               size_t src_path_len, dst_path_len;
 
                if (starts_with(info.buf, "::"))
                        die(N_("combined diff formats('-c' and '--cc') are "
@@ -349,17 +402,14 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                if (strbuf_getline_nul(&lpath, fp))
                        break;
                src_path = lpath.buf;
-               src_path_len = lpath.len;
 
                i++;
                if (status != 'C' && status != 'R') {
                        dst_path = src_path;
-                       dst_path_len = src_path_len;
                } else {
                        if (strbuf_getline_nul(&rpath, fp))
                                break;
                        dst_path = rpath.buf;
-                       dst_path_len = rpath.len;
                }
 
                if (S_ISGITLINK(lmode) || S_ISGITLINK(rmode)) {
@@ -377,27 +427,23 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                }
 
                if (S_ISLNK(lmode)) {
-                       char *content = read_sha1_file(loid.hash, &type, &size);
+                       char *content = get_symlink(&loid, src_path);
                        add_left_or_right(&symlinks2, src_path, content, 0);
                        free(content);
                }
 
                if (S_ISLNK(rmode)) {
-                       char *content = read_sha1_file(roid.hash, &type, &size);
+                       char *content = get_symlink(&roid, dst_path);
                        add_left_or_right(&symlinks2, dst_path, content, 1);
                        free(content);
                }
 
                if (lmode && status != 'C') {
-                       ce->ce_mode = lmode;
-                       oidcpy(&ce->oid, &loid);
-                       strcpy(ce->name, src_path);
-                       ce->ce_namelen = src_path_len;
-                       if (checkout_entry(ce, &lstate, NULL))
+                       if (checkout_path(lmode, &loid, src_path, &lstate))
                                return error("could not write '%s'", src_path);
                }
 
-               if (rmode) {
+               if (rmode && !S_ISLNK(rmode)) {
                        struct working_tree_entry *entry;
 
                        /* Avoid duplicate working_tree entries */
@@ -410,11 +456,7 @@ 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)) {
-                               ce->ce_mode = rmode;
-                               oidcpy(&ce->oid, &roid);
-                               strcpy(ce->name, dst_path);
-                               ce->ce_namelen = dst_path_len;
-                               if (checkout_entry(ce, &rstate, NULL))
+                               if (checkout_path(rmode, &roid, dst_path, &rstate))
                                        return error("could not write '%s'",
                                                     dst_path);
                        } else if (!is_null_oid(&roid)) {
@@ -584,7 +626,8 @@ static int run_dir_diff(const char *extcmd, int symlinks, const char *prefix,
                exit_cleanup(tmpdir, rc);
 
 finish:
-       free(ce);
+       free(lbase_dir);
+       free(rbase_dir);
        strbuf_release(&ldir);
        strbuf_release(&rdir);
        strbuf_release(&wtdir);
index 2a1c1c213f4092a274c562324c4d1820c58a6be2..366b9d13f929b7d299a31d9c53fb643220fe64e3 100644 (file)
@@ -50,7 +50,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
        char **pack_lockfile_ptr = NULL;
        struct child_process *conn;
        struct fetch_pack_args args;
-       struct sha1_array shallow = SHA1_ARRAY_INIT;
+       struct oid_array shallow = OID_ARRAY_INIT;
        struct string_list deepen_not = STRING_LIST_INIT_DUP;
 
        packet_trace_identity("fetch-pack");
index b5ad09d0460cc7a51f35a56a4304e6a1fb26d26e..5f2c2ab23e4cde17747147f23dcedaebe66c74a3 100644 (file)
@@ -421,7 +421,7 @@ static int s_update_ref(const char *action,
                        struct ref *ref,
                        int check_old)
 {
-       char msg[1024];
+       char *msg;
        char *rla = getenv("GIT_REFLOG_ACTION");
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
@@ -431,7 +431,7 @@ static int s_update_ref(const char *action,
                return 0;
        if (!rla)
                rla = default_rla.buf;
-       snprintf(msg, sizeof(msg), "%s: %s", rla, action);
+       msg = xstrfmt("%s: %s", rla, action);
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
@@ -449,11 +449,13 @@ static int s_update_ref(const char *action,
 
        ref_transaction_free(transaction);
        strbuf_release(&err);
+       free(msg);
        return 0;
 fail:
        ref_transaction_free(transaction);
        error("%s", err.buf);
        strbuf_release(&err);
+       free(msg);
        return df_conflict ? STORE_REF_ERROR_DF_CONFLICT
                           : STORE_REF_ERROR_OTHER;
 }
@@ -659,7 +661,7 @@ static int update_local_ref(struct ref *ref,
 
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(ref->new_oid.hash);
+                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref(msg, ref, 0);
                format_display(display, r ? '!' : '*', what,
                               r ? _("unable to update local ref") : NULL,
@@ -675,7 +677,7 @@ static int update_local_ref(struct ref *ref,
                strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(ref->new_oid.hash);
+                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("fast-forward", ref, 1);
                format_display(display, r ? '!' : ' ', quickref.buf,
                               r ? _("unable to update local ref") : NULL,
@@ -690,7 +692,7 @@ static int update_local_ref(struct ref *ref,
                strbuf_add_unique_abbrev(&quickref, ref->new_oid.hash, DEFAULT_ABBREV);
                if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
                    (recurse_submodules != RECURSE_SUBMODULES_ON))
-                       check_for_new_submodule_commits(ref->new_oid.hash);
+                       check_for_new_submodule_commits(&ref->new_oid);
                r = s_update_ref("forced-update", ref, 1);
                format_display(display, r ? '!' : '+', quickref.buf,
                               r ? _("unable to update local ref") : _("forced update"),
index df41fa035004e1cf8a0c7c9ff09081a47b944f38..eca365bf89bb64a71d02d21da29fc1e84204c1a1 100644 (file)
@@ -8,8 +8,8 @@
 static char const * const for_each_ref_usage[] = {
        N_("git for-each-ref [<options>] [<pattern>]"),
        N_("git for-each-ref [--points-at <object>]"),
-       N_("git for-each-ref [(--merged | --no-merged) [<object>]]"),
-       N_("git for-each-ref [--contains [<object>]]"),
+       N_("git for-each-ref [(--merged | --no-merged) [<commit>]]"),
+       N_("git for-each-ref [--contains [<commit>]] [--no-contains [<commit>]]"),
        NULL
 };
 
@@ -43,6 +43,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
                OPT_MERGED(&filter, N_("print only refs that are merged")),
                OPT_NO_MERGED(&filter, N_("print only refs that are not merged")),
                OPT_CONTAINS(&filter.with_commit, N_("print only refs which contain the commit")),
+               OPT_NO_CONTAINS(&filter.no_commit, N_("print only refs which don't contain the commit")),
                OPT_BOOL(0, "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
                OPT_END(),
        };
index f76e4163abbac825c00111504a040f8091d967d3..b5e13a45560f9338a65191c22d213f33052bf9b9 100644 (file)
@@ -771,6 +771,7 @@ int cmd_fsck(int argc, const char **argv, const char *prefix)
        }
 
        if (keep_cache_objects) {
+               verify_index_checksum = 1;
                read_cache();
                for (i = 0; i < active_nr; i++) {
                        unsigned int mode;
index c2c61a57bb359a37373ffd49aba79e5d5514a9fc..91f7696a85ec974d0ff5a1591cc404ed99efb7d7 100644 (file)
@@ -135,8 +135,6 @@ static int too_many_loose_objects(void)
         * distributed, we can check only one and get a reasonable
         * estimate.
         */
-       char path[PATH_MAX];
-       const char *objdir = get_object_directory();
        DIR *dir;
        struct dirent *ent;
        int auto_threshold;
@@ -146,11 +144,7 @@ static int too_many_loose_objects(void)
        if (gc_auto_threshold <= 0)
                return 0;
 
-       if (sizeof(path) <= snprintf(path, sizeof(path), "%s/17", objdir)) {
-               warning(_("insanely long object directory %.*s"), 50, objdir);
-               return 0;
-       }
-       dir = opendir(path);
+       dir = opendir(git_path("objects/17"));
        if (!dir)
                return 0;
 
@@ -238,7 +232,7 @@ static int need_to_gc(void)
 static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
 {
        static struct lock_file lock;
-       char my_host[128];
+       char my_host[HOST_NAME_MAX + 1];
        struct strbuf sb = STRBUF_INIT;
        struct stat st;
        uintmax_t pid;
@@ -250,15 +244,19 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
                /* already locked */
                return NULL;
 
-       if (gethostname(my_host, sizeof(my_host)))
+       if (xgethostname(my_host, sizeof(my_host)))
                xsnprintf(my_host, sizeof(my_host), "unknown");
 
        pidfile_path = git_pathdup("gc.pid");
        fd = hold_lock_file_for_update(&lock, pidfile_path,
                                       LOCK_DIE_ON_ERROR);
        if (!force) {
-               static char locking_host[128];
+               static char locking_host[HOST_NAME_MAX + 1];
+               static char *scan_fmt;
                int should_exit;
+
+               if (!scan_fmt)
+                       scan_fmt = xstrfmt("%s %%%dc", "%"SCNuMAX, HOST_NAME_MAX);
                fp = fopen(pidfile_path, "r");
                memset(locking_host, 0, sizeof(locking_host));
                should_exit =
@@ -274,7 +272,7 @@ static const char *lock_repo_for_gc(int force, pid_t* ret_pid)
                         * running.
                         */
                        time(NULL) - st.st_mtime <= 12 * 3600 &&
-                       fscanf(fp, "%"SCNuMAX" %127c", &pid, locking_host) == 2 &&
+                       fscanf(fp, scan_fmt, &pid, locking_host) == 2 &&
                        /* be gentle to concurrent "gc" on remote hosts */
                        (strcmp(locking_host, my_host) || !kill(pid, 0) || errno == EPERM);
                if (fp != NULL)
index 837836fb3e3bf9cdeb25edf8789428aaf813f49d..3ffb5b4e8176bbcd1ecf2e4ae2dc84aee968cd22 100644 (file)
@@ -310,10 +310,7 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
 {
        struct strbuf pathbuf = STRBUF_INIT;
 
-       if (opt->relative && opt->prefix_length) {
-               quote_path_relative(filename + tree_name_len, opt->prefix, &pathbuf);
-               strbuf_insert(&pathbuf, 0, filename, tree_name_len);
-       } else if (super_prefix) {
+       if (super_prefix) {
                strbuf_add(&pathbuf, filename, tree_name_len);
                strbuf_addstr(&pathbuf, super_prefix);
                strbuf_addstr(&pathbuf, filename + tree_name_len);
@@ -321,6 +318,13 @@ static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
                strbuf_addstr(&pathbuf, filename);
        }
 
+       if (opt->relative && opt->prefix_length) {
+               char *name = strbuf_detach(&pathbuf, NULL);
+               quote_path_relative(name + tree_name_len, opt->prefix, &pathbuf);
+               strbuf_insert(&pathbuf, 0, name, tree_name_len);
+               free(name);
+       }
+
 #ifndef NO_PTHREADS
        if (num_threads) {
                add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
@@ -345,12 +349,14 @@ static int grep_file(struct grep_opt *opt, const char *filename)
 {
        struct strbuf buf = STRBUF_INIT;
 
+       if (super_prefix)
+               strbuf_addstr(&buf, super_prefix);
+       strbuf_addstr(&buf, filename);
+
        if (opt->relative && opt->prefix_length) {
-               quote_path_relative(filename, opt->prefix, &buf);
-       } else {
-               if (super_prefix)
-                       strbuf_addstr(&buf, super_prefix);
-               strbuf_addstr(&buf, filename);
+               char *name = strbuf_detach(&buf, NULL);
+               quote_path_relative(name, opt->prefix, &buf);
+               free(name);
        }
 
 #ifndef NO_PTHREADS
@@ -399,13 +405,12 @@ static void run_pager(struct grep_opt *opt, const char *prefix)
 }
 
 static void compile_submodule_options(const struct grep_opt *opt,
-                                     const struct pathspec *pathspec,
+                                     const char **argv,
                                      int cached, int untracked,
                                      int opt_exclude, int use_index,
                                      int pattern_type_arg)
 {
        struct grep_pat *pattern;
-       int i;
 
        if (recurse_submodules)
                argv_array_push(&submodule_options, "--recurse-submodules");
@@ -523,9 +528,8 @@ static void compile_submodule_options(const struct grep_opt *opt,
 
        /* Add Pathspecs */
        argv_array_push(&submodule_options, "--");
-       for (i = 0; i < pathspec->nr; i++)
-               argv_array_push(&submodule_options,
-                               pathspec->items[i].original);
+       for (; *argv; argv++)
+               argv_array_push(&submodule_options, *argv);
 }
 
 /*
@@ -538,7 +542,7 @@ static int grep_submodule_launch(struct grep_opt *opt,
        int status, i;
        const char *end_of_base;
        const char *name;
-       struct work_item *w = opt->output_priv;
+       struct strbuf child_output = STRBUF_INIT;
 
        end_of_base = strchr(gs->name, ':');
        if (gs->identifier && end_of_base)
@@ -549,6 +553,11 @@ static int grep_submodule_launch(struct grep_opt *opt,
        prepare_submodule_repo_env(&cp.env_array);
        argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
 
+       if (opt->relative && opt->prefix_length)
+               argv_array_pushf(&cp.env_array, "%s=%s",
+                                GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
+                                opt->prefix);
+
        /* Add super prefix */
        argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
                         super_prefix ? super_prefix : "",
@@ -593,14 +602,16 @@ static int grep_submodule_launch(struct grep_opt *opt,
         * child process.  A '0' indicates a hit, a '1' indicates no hit and
         * anything else is an error.
         */
-       status = capture_command(&cp, &w->out, 0);
+       status = capture_command(&cp, &child_output, 0);
        if (status && (status != 1)) {
                /* flush the buffer */
-               write_or_die(1, w->out.buf, w->out.len);
+               write_or_die(1, child_output.buf, child_output.len);
                die("process for submodule '%s' failed with exit code: %d",
                    gs->name, status);
        }
 
+       opt->output(opt, child_output.buf, child_output.len);
+       strbuf_release(&child_output);
        /* invert the return code to make a hit equal to 1 */
        return !status;
 }
@@ -616,7 +627,7 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
 {
        if (!is_submodule_initialized(path))
                return 0;
-       if (!is_submodule_populated(path)) {
+       if (!is_submodule_populated_gently(path, NULL)) {
                /*
                 * If searching history, check for the presense of the
                 * submodule's gitdir before skipping the submodule.
@@ -641,19 +652,14 @@ static int grep_submodule(struct grep_opt *opt, const unsigned char *sha1,
        } else
 #endif
        {
-               struct work_item w;
+               struct grep_source gs;
                int hit;
 
-               grep_source_init(&w.source, GREP_SOURCE_SUBMODULE,
+               grep_source_init(&gs, GREP_SOURCE_SUBMODULE,
                                 filename, path, sha1);
-               strbuf_init(&w.out, 0);
-               opt->output_priv = &w;
-               hit = grep_submodule_launch(opt, &w.source);
-
-               write_or_die(1, w.out.buf, w.out.len);
+               hit = grep_submodule_launch(opt, &gs);
 
-               grep_source_clear(&w.source);
-               strbuf_release(&w.out);
+               grep_source_clear(&gs);
                return hit;
        }
 }
@@ -979,7 +985,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                OPT_SET_INT(0, "exclude-standard", &opt_exclude,
                            N_("ignore files specified via '.gitignore'"), 1),
                OPT_BOOL(0, "recurse-submodules", &recurse_submodules,
-                        N_("recursivley search in each submodule")),
+                        N_("recursively search in each submodule")),
                OPT_STRING(0, "parent-basename", &parent_basename,
                           N_("basename"),
                           N_("prepend parent project's basename to output")),
@@ -1236,7 +1242,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
 
        if (recurse_submodules) {
                gitmodules_config();
-               compile_submodule_options(&opt, &pathspec, cached, untracked,
+               compile_submodule_options(&opt, argv + i, cached, untracked,
                                          opt_exclude, use_index,
                                          pattern_type_arg);
        }
@@ -1293,6 +1299,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                hit |= wait_all();
        if (hit && show_in_pager)
                run_pager(&opt, prefix);
+       clear_pathspec(&pathspec);
        free_grep_patterns(&opt);
        return !hit;
 }
index 9028e1fdccea2ad44a76792adc6e335fb44cfb5c..bbeaf20bcca1ae1e9bfa55b5a8e4adbed83c00e8 100644 (file)
@@ -102,7 +102,6 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
                OPT_END()
        };
        int i;
-       int prefix_length = -1;
        const char *errstr = NULL;
 
        argc = parse_options(argc, argv, NULL, hash_object_options,
@@ -113,9 +112,8 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
        else
                prefix = setup_git_directory_gently(&nongit);
 
-       prefix_length = prefix ? strlen(prefix) : 0;
        if (vpath && prefix)
-               vpath = prefix_filename(prefix, prefix_length, vpath);
+               vpath = xstrdup(prefix_filename(prefix, vpath));
 
        git_config(git_default_config, NULL);
 
@@ -144,11 +142,13 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)
 
        for (i = 0 ; i < argc; i++) {
                const char *arg = argv[i];
+               char *to_free = NULL;
 
-               if (0 <= prefix_length)
-                       arg = prefix_filename(prefix, prefix_length, arg);
+               if (prefix)
+                       arg = to_free = prefix_filename(prefix, arg);
                hash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,
                            flags, literally);
+               free(to_free);
        }
 
        if (stdin_paths)
index f4b87c6c9f901e5834ce9e50db089fb487c6eafc..4ff567db475573f8f45830983b7d3716d6832b4b 100644 (file)
@@ -307,14 +307,15 @@ static const char *open_pack_file(const char *pack_name)
        if (from_stdin) {
                input_fd = 0;
                if (!pack_name) {
-                       static char tmp_file[PATH_MAX];
-                       output_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
+                       struct strbuf tmp_file = STRBUF_INIT;
+                       output_fd = odb_mkstemp(&tmp_file,
                                                "pack/tmp_pack_XXXXXX");
-                       pack_name = xstrdup(tmp_file);
-               } else
+                       pack_name = strbuf_detach(&tmp_file, NULL);
+               } else {
                        output_fd = open(pack_name, O_CREAT|O_EXCL|O_RDWR, 0600);
-               if (output_fd < 0)
-                       die_errno(_("unable to create '%s'"), pack_name);
+                       if (output_fd < 0)
+                               die_errno(_("unable to create '%s'"), pack_name);
+               }
                nothread_data.pack_fd = output_fd;
        } else {
                input_fd = open(pack_name, O_RDONLY);
@@ -809,6 +810,8 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                unsigned long has_size;
                read_lock();
                has_type = sha1_object_info(sha1, &has_size);
+               if (has_type < 0)
+                       die(_("cannot read existing object info %s"), sha1_to_hex(sha1));
                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);
@@ -1386,7 +1389,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                  unsigned char *sha1)
 {
        const char *report = "pack";
-       char name[PATH_MAX];
+       struct strbuf pack_name = STRBUF_INIT;
+       struct strbuf index_name = STRBUF_INIT;
+       struct strbuf keep_name_buf = STRBUF_INIT;
        int err;
 
        if (!from_stdin) {
@@ -1402,14 +1407,13 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                int keep_fd, keep_msg_len = strlen(keep_msg);
 
                if (!keep_name)
-                       keep_fd = odb_pack_keep(name, sizeof(name), sha1);
-               else
-                       keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+                       keep_name = odb_pack_name(&keep_name_buf, sha1, "keep");
 
+               keep_fd = odb_pack_keep(keep_name);
                if (keep_fd < 0) {
                        if (errno != EEXIST)
                                die_errno(_("cannot write keep file '%s'"),
-                                         keep_name ? keep_name : name);
+                                         keep_name);
                } else {
                        if (keep_msg_len > 0) {
                                write_or_die(keep_fd, keep_msg, keep_msg_len);
@@ -1417,28 +1421,22 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        }
                        if (close(keep_fd) != 0)
                                die_errno(_("cannot close written keep file '%s'"),
-                                         keep_name ? keep_name : name);
+                                         keep_name);
                        report = "keep";
                }
        }
 
        if (final_pack_name != curr_pack_name) {
-               if (!final_pack_name) {
-                       snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
-                                get_object_directory(), sha1_to_hex(sha1));
-                       final_pack_name = name;
-               }
+               if (!final_pack_name)
+                       final_pack_name = odb_pack_name(&pack_name, sha1, "pack");
                if (finalize_object_file(curr_pack_name, final_pack_name))
                        die(_("cannot store pack file"));
        } else if (from_stdin)
                chmod(final_pack_name, 0444);
 
        if (final_index_name != curr_index_name) {
-               if (!final_index_name) {
-                       snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
-                                get_object_directory(), sha1_to_hex(sha1));
-                       final_index_name = name;
-               }
+               if (!final_index_name)
+                       final_index_name = odb_pack_name(&index_name, sha1, "idx");
                if (finalize_object_file(curr_index_name, final_index_name))
                        die(_("cannot store index file"));
        } else
@@ -1447,10 +1445,11 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
        if (!from_stdin) {
                printf("%s\n", sha1_to_hex(sha1));
        } else {
-               char buf[48];
-               int len = snprintf(buf, sizeof(buf), "%s\t%s\n",
-                                  report, sha1_to_hex(sha1));
-               write_or_die(1, buf, len);
+               struct strbuf buf = STRBUF_INIT;
+
+               strbuf_addf(&buf, "%s\t%s\n", report, sha1_to_hex(sha1));
+               write_or_die(1, buf.buf, buf.len);
+               strbuf_release(&buf);
 
                /*
                 * Let's just mimic git-unpack-objects here and write
@@ -1464,6 +1463,10 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        input_offset += err;
                }
        }
+
+       strbuf_release(&index_name);
+       strbuf_release(&pack_name);
+       strbuf_release(&keep_name_buf);
 }
 
 static int git_index_pack_config(const char *k, const char *v, void *cb)
index 281af8c1ecb04c7be05d9a17a1d5f3bd43420c85..b3b10cc1edba2f6d77fdf4b12c328a7968fb5499 100644 (file)
@@ -52,6 +52,11 @@ struct line_opt_callback_data {
        struct string_list args;
 };
 
+static int auto_decoration_style(void)
+{
+       return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
+}
+
 static int parse_decoration_style(const char *var, const char *value)
 {
        switch (git_config_maybe_bool(var, value)) {
@@ -67,7 +72,7 @@ static int parse_decoration_style(const char *var, const char *value)
        else if (!strcmp(value, "short"))
                return DECORATE_SHORT_REFS;
        else if (!strcmp(value, "auto"))
-               return (isatty(1) || pager_in_use()) ? DECORATE_SHORT_REFS : 0;
+               return auto_decoration_style();
        return -1;
 }
 
@@ -405,6 +410,8 @@ 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);
@@ -1084,8 +1091,7 @@ static const char *set_outdir(const char *prefix, const char *output_directory)
        if (!output_directory)
                return prefix;
 
-       return xstrdup(prefix_filename(prefix, outdir_offset,
-                                      output_directory));
+       return prefix_filename(prefix, output_directory);
 }
 
 static const char * const builtin_format_patch_usage[] = {
index 1c0f057d02c55c8449bc9da1f1f01d509bbca8ba..a6c70dbe9ec84921108a85485a12111c854833b8 100644 (file)
@@ -15,6 +15,7 @@
 #include "string-list.h"
 #include "pathspec.h"
 #include "run-command.h"
+#include "submodule.h"
 
 static int abbrev;
 static int show_deleted;
@@ -30,7 +31,7 @@ static int line_terminator = '\n';
 static int debug_mode;
 static int show_eol;
 static int recurse_submodules;
-static struct argv_array submodules_options = ARGV_ARRAY_INIT;
+static struct argv_array submodule_options = ARGV_ARRAY_INIT;
 
 static const char *prefix;
 static const char *super_prefix;
@@ -172,20 +173,27 @@ static void show_killed_files(struct dir_struct *dir)
 /*
  * Compile an argv_array with all of the options supported by --recurse_submodules
  */
-static void compile_submodule_options(const struct dir_struct *dir, int show_tag)
+static void compile_submodule_options(const char **argv,
+                                     const struct dir_struct *dir,
+                                     int show_tag)
 {
        if (line_terminator == '\0')
-               argv_array_push(&submodules_options, "-z");
+               argv_array_push(&submodule_options, "-z");
        if (show_tag)
-               argv_array_push(&submodules_options, "-t");
+               argv_array_push(&submodule_options, "-t");
        if (show_valid_bit)
-               argv_array_push(&submodules_options, "-v");
+               argv_array_push(&submodule_options, "-v");
        if (show_cached)
-               argv_array_push(&submodules_options, "--cached");
+               argv_array_push(&submodule_options, "--cached");
        if (show_eol)
-               argv_array_push(&submodules_options, "--eol");
+               argv_array_push(&submodule_options, "--eol");
        if (debug_mode)
-               argv_array_push(&submodules_options, "--debug");
+               argv_array_push(&submodule_options, "--debug");
+
+       /* Add Pathspecs */
+       argv_array_push(&submodule_options, "--");
+       for (; *argv; argv++)
+               argv_array_push(&submodule_options, *argv);
 }
 
 /**
@@ -195,8 +203,15 @@ static void show_gitlink(const struct cache_entry *ce)
 {
        struct child_process cp = CHILD_PROCESS_INIT;
        int status;
-       int i;
+       char *dir;
+
+       prepare_submodule_repo_env(&cp.env_array);
+       argv_array_push(&cp.env_array, GIT_DIR_ENVIRONMENT);
 
+       if (prefix_len)
+               argv_array_pushf(&cp.env_array, "%s=%s",
+                                GIT_TOPLEVEL_PREFIX_ENVIRONMENT,
+                                prefix);
        argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
                         super_prefix ? super_prefix : "",
                         ce->name);
@@ -204,20 +219,13 @@ static void show_gitlink(const struct cache_entry *ce)
        argv_array_push(&cp.args, "--recurse-submodules");
 
        /* add supported options */
-       argv_array_pushv(&cp.args, submodules_options.argv);
-
-       /*
-        * Pass in the original pathspec args.  The submodule will be
-        * responsible for prepending the 'submodule_prefix' prior to comparing
-        * against the pathspec for matches.
-        */
-       argv_array_push(&cp.args, "--");
-       for (i = 0; i < pathspec.nr; i++)
-               argv_array_push(&cp.args, pathspec.items[i].original);
+       argv_array_pushv(&cp.args, submodule_options.argv);
 
        cp.git_cmd = 1;
-       cp.dir = ce->name;
+       dir = mkpathdup("%s/%s", get_git_work_tree(), ce->name);
+       cp.dir = dir;
        status = run_command(&cp);
+       free(dir);
        if (status)
                exit(status);
 }
@@ -604,7 +612,7 @@ int cmd_ls_files(int argc, const char **argv, const char *cmd_prefix)
                setup_work_tree();
 
        if (recurse_submodules)
-               compile_submodule_options(&dir, show_tag);
+               compile_submodule_options(argv, &dir, show_tag);
 
        if (recurse_submodules &&
            (show_stage || show_deleted || show_others || show_unmerged ||
index 66cdd45cc10afd37f93f260688700408d11b1f5f..b2d7d5ce6841cf9cb9e310c659aac043ad00cfb0 100644 (file)
@@ -17,17 +17,19 @@ static const char * const ls_remote_usage[] = {
 static int tail_match(const char **pattern, const char *path)
 {
        const char *p;
-       char pathbuf[PATH_MAX];
+       char *pathbuf;
 
        if (!pattern)
                return 1; /* no restriction */
 
-       if (snprintf(pathbuf, sizeof(pathbuf), "/%s", path) > sizeof(pathbuf))
-               return error("insanely long ref %.*s...", 20, path);
+       pathbuf = xstrfmt("/%s", path);
        while ((p = *(pattern++)) != NULL) {
-               if (!wildmatch(p, pathbuf, 0, NULL))
+               if (!wildmatch(p, pathbuf, 0, NULL)) {
+                       free(pathbuf);
                        return 1;
+               }
        }
+       free(pathbuf);
        return 0;
 }
 
index e3b62f2fc744d340de5e3d9efad12b5de93cd55c..cfb667a594c8452b8f4259bb3da1976965906f95 100644 (file)
 static const char mailinfo_usage[] =
        "git mailinfo [-k | -b] [-m | --message-id] [-u | --encoding=<encoding> | -n] [--scissors | --no-scissors] <msg> <patch> < mail >info";
 
-static char *prefix_copy(const char *prefix, const char *filename)
-{
-       if (!prefix || is_absolute_path(filename))
-               return xstrdup(filename);
-       return xstrdup(prefix_filename(prefix, strlen(prefix), filename));
-}
-
 int cmd_mailinfo(int argc, const char **argv, const char *prefix)
 {
        const char *def_charset;
@@ -60,8 +53,8 @@ int cmd_mailinfo(int argc, const char **argv, const char *prefix)
        mi.input = stdin;
        mi.output = stdout;
 
-       msgfile = prefix_copy(prefix, argv[1]);
-       patchfile = prefix_copy(prefix, argv[2]);
+       msgfile = prefix_filename(prefix, argv[1]);
+       patchfile = prefix_filename(prefix, argv[2]);
 
        status = !!mailinfo(&mi, msgfile, patchfile);
        clear_mailinfo(&mi);
index 13e22a2f0be73ec7e521a02d1f36c414e77a8700..47dde7c39c922c77bf388e87db27618d09f5bb74 100644 (file)
@@ -28,7 +28,6 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
        xmparam_t xmp = {{0}};
        int ret = 0, i = 0, to_stdout = 0;
        int quiet = 0;
-       int prefixlen = 0;
        struct option options[] = {
                OPT_BOOL('p', "stdout", &to_stdout, N_("send results to standard output")),
                OPT_SET_INT(0, "diff3", &xmp.style, N_("use a diff3 based merge"), XDL_MERGE_DIFF3),
@@ -65,15 +64,19 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                        return error_errno("failed to redirect stderr to /dev/null");
        }
 
-       if (prefix)
-               prefixlen = strlen(prefix);
-
        for (i = 0; i < 3; i++) {
-               const char *fname = prefix_filename(prefix, prefixlen, argv[i]);
+               char *fname;
+               int ret;
+
                if (!names[i])
                        names[i] = argv[i];
-               if (read_mmfile(mmfs + i, fname))
+
+               fname = prefix_filename(prefix, argv[i]);
+               ret = read_mmfile(mmfs + i, fname);
+               free(fname);
+               if (ret)
                        return -1;
+
                if (mmfs[i].size > MAX_XDIFF_SIZE ||
                    buffer_is_binary(mmfs[i].ptr, mmfs[i].size))
                        return error("Cannot merge binary files: %s",
@@ -90,7 +93,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
 
        if (ret >= 0) {
                const char *filename = argv[0];
-               const char *fpath = prefix_filename(prefix, prefixlen, argv[0]);
+               char *fpath = prefix_filename(prefix, argv[0]);
                FILE *f = to_stdout ? stdout : fopen(fpath, "wb");
 
                if (!f)
@@ -102,6 +105,7 @@ int cmd_merge_file(int argc, const char **argv, const char *prefix)
                else if (fclose(f))
                        ret = error_errno("Could not close %s", filename);
                free(result.ptr);
+               free(fpath);
        }
 
        if (ret > 127)
index 2d1b6db6bd7a33f838a50deceb108ee1ad468db1..c99443b095bd8abbe02d4ea87a452d916681c53f 100644 (file)
@@ -9,7 +9,7 @@ static int merge_entry(int pos, const char *path)
 {
        int found;
        const char *arguments[] = { pgm, "", "", "", path, "", "", "", NULL };
-       char hexbuf[4][GIT_SHA1_HEXSZ + 1];
+       char hexbuf[4][GIT_MAX_HEXSZ + 1];
        char ownbuf[4][60];
 
        if (pos >= active_nr)
index 7554b8d4127ada8261d1f913c6215e93ca42ef7d..703827f00668ca0df807f35e300f0927d00075cd 100644 (file)
@@ -44,7 +44,6 @@ struct strategy {
 
 static const char * const builtin_merge_usage[] = {
        N_("git merge [<options>] [<commit>...]"),
-       N_("git merge [<options>] <msg> HEAD <commit>"),
        N_("git merge --abort"),
        N_("git merge --continue"),
        NULL
@@ -634,9 +633,10 @@ static void write_tree_trivial(struct object_id *oid)
 
 static int try_merge_strategy(const char *strategy, struct commit_list *common,
                              struct commit_list *remoteheads,
-                             struct commit *head, const char *head_arg)
+                             struct commit *head)
 {
        static struct lock_file lock;
+       const char *head_arg = "HEAD";
 
        hold_locked_index(&lock, LOCK_DIE_ON_ERROR);
        refresh_cache(REFRESH_QUIET);
@@ -853,24 +853,6 @@ static int suggest_conflicts(void)
        return 1;
 }
 
-static struct commit *is_old_style_invocation(int argc, const char **argv,
-                                             const struct object_id *head)
-{
-       struct commit *second_token = NULL;
-       if (argc > 2) {
-               struct object_id second_oid;
-
-               if (get_oid(argv[1], &second_oid))
-                       return NULL;
-               second_token = lookup_commit_reference_gently(second_oid.hash, 0);
-               if (!second_token)
-                       die(_("'%s' is not a commit"), argv[1]);
-               if (oidcmp(&second_token->object.oid, head))
-                       return NULL;
-       }
-       return second_token;
-}
-
 static int evaluate_result(void)
 {
        int cnt = 0;
@@ -1120,7 +1102,6 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        struct object_id result_tree, stash, head_oid;
        struct commit *head_commit;
        struct strbuf buf = STRBUF_INIT;
-       const char *head_arg;
        int i, ret = 0, head_subsumed;
        int best_cnt = -1, merge_was_ok = 0, automerge_was_ok = 0;
        struct commit_list *common = NULL;
@@ -1260,34 +1241,12 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        }
 
        /*
-        * This could be traditional "merge <msg> HEAD <commit>..."  and
-        * the way we can tell it is to see if the second token is HEAD,
-        * but some people might have misused the interface and used a
-        * commit-ish that is the same as HEAD there instead.
-        * Traditional format never would have "-m" so it is an
-        * additional safety measure to check for it.
+        * All the rest are the commits being merged; prepare
+        * the standard merge summary message to be appended
+        * to the given message.
         */
-       if (!have_message &&
-           is_old_style_invocation(argc, argv, &head_commit->object.oid)) {
-               warning("old-style 'git merge <msg> HEAD <commit>' is deprecated.");
-               strbuf_addstr(&merge_msg, argv[0]);
-               head_arg = argv[1];
-               argv += 2;
-               argc -= 2;
-               remoteheads = collect_parents(head_commit, &head_subsumed,
-                                             argc, argv, NULL);
-       } else {
-               /* We are invoked directly as the first-class UI. */
-               head_arg = "HEAD";
-
-               /*
-                * All the rest are the commits being merged; prepare
-                * the standard merge summary message to be appended
-                * to the given message.
-                */
-               remoteheads = collect_parents(head_commit, &head_subsumed,
-                                             argc, argv, &merge_msg);
-       }
+       remoteheads = collect_parents(head_commit, &head_subsumed,
+                                     argc, argv, &merge_msg);
 
        if (!head_commit || !argc)
                usage_with_options(builtin_merge_usage,
@@ -1296,7 +1255,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (verify_signatures) {
                for (p = remoteheads; p; p = p->next) {
                        struct commit *commit = p->item;
-                       char hex[GIT_SHA1_HEXSZ + 1];
+                       char hex[GIT_MAX_HEXSZ + 1];
                        struct signature_check signature_check;
                        memset(&signature_check, 0, sizeof(signature_check));
 
@@ -1513,7 +1472,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
                ret = try_merge_strategy(use_strategies[i]->name,
                                         common, remoteheads,
-                                        head_commit, head_arg);
+                                        head_commit);
                if (!option_commit && !ret) {
                        merge_was_ok = 1;
                        /*
@@ -1583,7 +1542,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
                try_merge_strategy(best_strategy, common, remoteheads,
-                                  head_commit, head_arg);
+                                  head_commit);
        }
 
        if (squash)
index 8bdc3eaa6fa4472d8352e7de10ef9d816e8ca83f..92a5d8a5d263f78afaa2f426135998348dae53b8 100644 (file)
@@ -238,10 +238,9 @@ static const char *get_exact_ref_match(const struct object *o)
        return NULL;
 }
 
-/* returns a static buffer */
-static const char *get_rev_name(const struct object *o)
+/* may return a constant string or use "buf" as scratch space */
+static const char *get_rev_name(const struct object *o, struct strbuf *buf)
 {
-       static char buffer[1024];
        struct rev_name *n;
        struct commit *c;
 
@@ -258,10 +257,9 @@ static const char *get_rev_name(const struct object *o)
                int len = strlen(n->tip_name);
                if (len > 2 && !strcmp(n->tip_name + len - 2, "^0"))
                        len -= 2;
-               snprintf(buffer, sizeof(buffer), "%.*s~%d", len, n->tip_name,
-                               n->generation);
-
-               return buffer;
+               strbuf_reset(buf);
+               strbuf_addf(buf, "%.*s~%d", len, n->tip_name, n->generation);
+               return buf->buf;
        }
 }
 
@@ -271,10 +269,11 @@ static void show_name(const struct object *obj,
 {
        const char *name;
        const struct object_id *oid = &obj->oid;
+       struct strbuf buf = STRBUF_INIT;
 
        if (!name_only)
                printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
-       name = get_rev_name(obj);
+       name = get_rev_name(obj, &buf);
        if (name)
                printf("%s\n", name);
        else if (allow_undefined)
@@ -283,6 +282,7 @@ static void show_name(const struct object *obj,
                printf("%s\n", find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
        else
                die("cannot describe '%s'", oid_to_hex(oid));
+       strbuf_release(&buf);
 }
 
 static char const * const name_rev_usage[] = {
@@ -294,6 +294,7 @@ static char const * const name_rev_usage[] = {
 
 static void name_rev_line(char *p, struct name_ref_data *data)
 {
+       struct strbuf buf = STRBUF_INIT;
        int forty = 0;
        char *p_start;
        for (p_start = p; *p; p++) {
@@ -314,7 +315,7 @@ static void name_rev_line(char *p, struct name_ref_data *data)
                                struct object *o =
                                        lookup_object(sha1);
                                if (o)
-                                       name = get_rev_name(o);
+                                       name = get_rev_name(o, &buf);
                        }
                        *(p+1) = c;
 
@@ -332,6 +333,8 @@ static void name_rev_line(char *p, struct name_ref_data *data)
        /* flush */
        if (p_start != p)
                fwrite(p_start, p - p_start, 1, stdout);
+
+       strbuf_release(&buf);
 }
 
 int cmd_name_rev(int argc, const char **argv, const char *prefix)
index 0513f7455dccd15e6ec1c2c1e1d847e66af027a7..7b891471c461ddc3cd2b9d3186cc8b005f68ca2b 100644 (file)
@@ -554,7 +554,7 @@ static int append_edit(int argc, const char **argv, const char *prefix)
        struct notes_tree *t;
        unsigned char object[20], new_note[20];
        const unsigned char *note;
-       char logmsg[100];
+       char *logmsg;
        const char * const *usage;
        struct note_data d = { 0, 0, NULL, STRBUF_INIT };
        struct option options[] = {
@@ -618,17 +618,16 @@ static int append_edit(int argc, const char **argv, const char *prefix)
                write_note_data(&d, new_note);
                if (add_note(t, object, new_note, combine_notes_overwrite))
                        die("BUG: combine_notes_overwrite failed");
-               snprintf(logmsg, sizeof(logmsg), "Notes added by 'git notes %s'",
-                       argv[0]);
+               logmsg = xstrfmt("Notes added by 'git notes %s'", argv[0]);
        } else {
                fprintf(stderr, _("Removing note for object %s\n"),
                        sha1_to_hex(object));
                remove_note(t, object);
-               snprintf(logmsg, sizeof(logmsg), "Notes removed by 'git notes %s'",
-                       argv[0]);
+               logmsg = xstrfmt("Notes removed by 'git notes %s'", argv[0]);
        }
        commit_notes(t, logmsg);
 
+       free(logmsg);
        free_note_data(&d);
        free_notes(t);
        return 0;
index 16517f26375b1a0ebe58b628b7741c7f5e77bc08..0fe35d1b5aebd74116d66d5414bdabf19baf7d97 100644 (file)
@@ -239,7 +239,8 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
                                           unsigned long limit, int usable_delta)
 {
        unsigned long size, datalen;
-       unsigned char header[10], dheader[10];
+       unsigned char header[MAX_PACK_OBJECT_HEADER],
+                     dheader[MAX_PACK_OBJECT_HEADER];
        unsigned hdrlen;
        enum object_type type;
        void *buf;
@@ -286,7 +287,8 @@ static unsigned long write_no_reuse_object(struct sha1file *f, struct object_ent
         * The object header is a byte of 'type' followed by zero or
         * more bytes of length.
         */
-       hdrlen = encode_in_pack_object_header(type, size, header);
+       hdrlen = encode_in_pack_object_header(header, sizeof(header),
+                                             type, size);
 
        if (type == OBJ_OFS_DELTA) {
                /*
@@ -352,13 +354,15 @@ static off_t write_reuse_object(struct sha1file *f, struct object_entry *entry,
        off_t offset;
        enum object_type type = entry->type;
        off_t datalen;
-       unsigned char header[10], dheader[10];
+       unsigned char header[MAX_PACK_OBJECT_HEADER],
+                     dheader[MAX_PACK_OBJECT_HEADER];
        unsigned hdrlen;
 
        if (entry->delta)
                type = (allow_ofs_delta && entry->delta->idx.offset) ?
                        OBJ_OFS_DELTA : OBJ_REF_DELTA;
-       hdrlen = encode_in_pack_object_header(type, entry->size, header);
+       hdrlen = encode_in_pack_object_header(header, sizeof(header),
+                                             type, entry->size);
 
        offset = entry->in_pack_offset;
        revidx = find_pack_revindex(p, offset);
@@ -2668,16 +2672,16 @@ static int has_sha1_pack_kept_or_nonlocal(const unsigned char *sha1)
  *
  * This is filled by get_object_list.
  */
-static struct sha1_array recent_objects;
+static struct oid_array recent_objects;
 
-static int loosened_object_can_be_discarded(const unsigned char *sha1,
+static int loosened_object_can_be_discarded(const struct object_id *oid,
                                            unsigned long mtime)
 {
        if (!unpack_unreachable_expiration)
                return 0;
        if (mtime > unpack_unreachable_expiration)
                return 0;
-       if (sha1_array_lookup(&recent_objects, sha1) >= 0)
+       if (oid_array_lookup(&recent_objects, oid) >= 0)
                return 0;
        return 1;
 }
@@ -2686,7 +2690,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
 {
        struct packed_git *p;
        uint32_t i;
-       const unsigned char *sha1;
+       struct object_id oid;
 
        for (p = packed_git; p; p = p->next) {
                if (!p->pack_local || p->pack_keep)
@@ -2696,11 +2700,11 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
                        die("cannot open pack index");
 
                for (i = 0; i < p->num_objects; i++) {
-                       sha1 = nth_packed_object_sha1(p, i);
-                       if (!packlist_find(&to_pack, sha1, NULL) &&
-                           !has_sha1_pack_kept_or_nonlocal(sha1) &&
-                           !loosened_object_can_be_discarded(sha1, p->mtime))
-                               if (force_object_loose(sha1, p->mtime))
+                       nth_packed_object_oid(&oid, p, i);
+                       if (!packlist_find(&to_pack, oid.hash, NULL) &&
+                           !has_sha1_pack_kept_or_nonlocal(oid.hash) &&
+                           !loosened_object_can_be_discarded(&oid, p->mtime))
+                               if (force_object_loose(oid.hash, p->mtime))
                                        die("unable to force loose object");
                }
        }
@@ -2739,12 +2743,12 @@ static void record_recent_object(struct object *obj,
                                 const char *name,
                                 void *data)
 {
-       sha1_array_append(&recent_objects, obj->oid.hash);
+       oid_array_append(&recent_objects, &obj->oid);
 }
 
 static void record_recent_commit(struct commit *commit, void *data)
 {
-       sha1_array_append(&recent_objects, commit->object.oid.hash);
+       oid_array_append(&recent_objects, &commit->object.oid);
 }
 
 static void get_object_list(int ac, const char **av)
@@ -2812,7 +2816,7 @@ static void get_object_list(int ac, const char **av)
        if (unpack_unreachable)
                loosen_unused_packed_objects(&revs);
 
-       sha1_array_clear(&recent_objects);
+       oid_array_clear(&recent_objects);
 }
 
 static int option_parse_index_version(const struct option *opt,
index 39f9a55d16736d93b43e6aa16a33634039e55de4..b106a392a481570d4fda5a1116c523cde34557ba 100644 (file)
@@ -17,5 +17,5 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix)
        };
        if (parse_options(argc, argv, prefix, opts, pack_refs_usage, 0))
                usage_with_options(pack_refs_usage, opts);
-       return pack_refs(flags);
+       return refs_pack_refs(get_main_ref_store(), flags);
 }
index a84d0003a3087c842b6eab6a987a93eebaddec79..81552e02e412b53fcb50ffa6a4d5949acdaac79c 100644 (file)
@@ -55,7 +55,7 @@ static int scan_hunk_header(const char *p, int *p_before, int *p_after)
 
 static void flush_one_hunk(struct object_id *result, git_SHA_CTX *ctx)
 {
-       unsigned char hash[GIT_SHA1_RAWSZ];
+       unsigned char hash[GIT_MAX_RAWSZ];
        unsigned short carry = 0;
        int i;
 
index 3ecb881b0bcacbf1a453bf9a6cb95ae00cdecb1d..d8aa26d8abe4e436f95f4476133967cf17f29988 100644 (file)
@@ -330,21 +330,21 @@ static int git_pull_config(const char *var, const char *value, void *cb)
  * Appends merge candidates from FETCH_HEAD that are not marked not-for-merge
  * into merge_heads.
  */
-static void get_merge_heads(struct sha1_array *merge_heads)
+static void get_merge_heads(struct oid_array *merge_heads)
 {
        const char *filename = git_path("FETCH_HEAD");
        FILE *fp;
        struct strbuf sb = STRBUF_INIT;
-       unsigned char sha1[GIT_SHA1_RAWSZ];
+       struct object_id oid;
 
        if (!(fp = fopen(filename, "r")))
                die_errno(_("could not open '%s' for reading"), filename);
        while (strbuf_getline_lf(&sb, fp) != EOF) {
-               if (get_sha1_hex(sb.buf, sha1))
+               if (get_oid_hex(sb.buf, &oid))
                        continue;  /* invalid line: does not start with SHA1 */
                if (starts_with(sb.buf + GIT_SHA1_HEXSZ, "\tnot-for-merge\t"))
                        continue;  /* ref is not-for-merge */
-               sha1_array_append(merge_heads, sha1);
+               oid_array_append(merge_heads, &oid);
        }
        fclose(fp);
        strbuf_release(&sb);
@@ -514,8 +514,8 @@ static int run_fetch(const char *repo, const char **refspecs)
 /**
  * "Pulls into void" by branching off merge_head.
  */
-static int pull_into_void(const unsigned char *merge_head,
-               const unsigned char *curr_head)
+static int pull_into_void(const struct object_id *merge_head,
+               const struct object_id *curr_head)
 {
        /*
         * Two-way merge: we treat the index as based on an empty tree,
@@ -523,10 +523,10 @@ static int pull_into_void(const unsigned char *merge_head,
         * index/worktree changes that the user already made on the unborn
         * branch.
         */
-       if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head, 0))
+       if (checkout_fast_forward(EMPTY_TREE_SHA1_BIN, merge_head->hash, 0))
                return 1;
 
-       if (update_ref("initial pull", "HEAD", merge_head, curr_head, 0, UPDATE_REFS_DIE_ON_ERR))
+       if (update_ref("initial pull", "HEAD", merge_head->hash, curr_head->hash, 0, UPDATE_REFS_DIE_ON_ERR))
                return 1;
 
        return 0;
@@ -647,7 +647,7 @@ static const char *get_tracking_branch(const char *remote, const char *refspec)
  * current branch forked from its remote tracking branch. Returns 0 on success,
  * -1 on failure.
  */
-static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
+static int get_rebase_fork_point(struct object_id *fork_point, const char *repo,
                const char *refspec)
 {
        int ret;
@@ -678,7 +678,7 @@ static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
        if (ret)
                goto cleanup;
 
-       ret = get_sha1_hex(sb.buf, fork_point);
+       ret = get_oid_hex(sb.buf, fork_point);
        if (ret)
                goto cleanup;
 
@@ -691,24 +691,24 @@ static int get_rebase_fork_point(unsigned char *fork_point, const char *repo,
  * Sets merge_base to the octopus merge base of curr_head, merge_head and
  * fork_point. Returns 0 if a merge base is found, 1 otherwise.
  */
-static int get_octopus_merge_base(unsigned char *merge_base,
-               const unsigned char *curr_head,
-               const unsigned char *merge_head,
-               const unsigned char *fork_point)
+static int get_octopus_merge_base(struct object_id *merge_base,
+               const struct object_id *curr_head,
+               const struct object_id *merge_head,
+               const struct object_id *fork_point)
 {
        struct commit_list *revs = NULL, *result;
 
-       commit_list_insert(lookup_commit_reference(curr_head), &revs);
-       commit_list_insert(lookup_commit_reference(merge_head), &revs);
-       if (!is_null_sha1(fork_point))
-               commit_list_insert(lookup_commit_reference(fork_point), &revs);
+       commit_list_insert(lookup_commit_reference(curr_head->hash), &revs);
+       commit_list_insert(lookup_commit_reference(merge_head->hash), &revs);
+       if (!is_null_oid(fork_point))
+               commit_list_insert(lookup_commit_reference(fork_point->hash), &revs);
 
        result = reduce_heads(get_octopus_merge_bases(revs));
        free_commit_list(revs);
        if (!result)
                return 1;
 
-       hashcpy(merge_base, result->item->object.oid.hash);
+       oidcpy(merge_base, &result->item->object.oid);
        return 0;
 }
 
@@ -717,16 +717,16 @@ static int get_octopus_merge_base(unsigned char *merge_base,
  * fork point calculated by get_rebase_fork_point(), runs git-rebase with the
  * appropriate arguments and returns its exit status.
  */
-static int run_rebase(const unsigned char *curr_head,
-               const unsigned char *merge_head,
-               const unsigned char *fork_point)
+static int run_rebase(const struct object_id *curr_head,
+               const struct object_id *merge_head,
+               const struct object_id *fork_point)
 {
        int ret;
-       unsigned char oct_merge_base[GIT_SHA1_RAWSZ];
+       struct object_id oct_merge_base;
        struct argv_array args = ARGV_ARRAY_INIT;
 
-       if (!get_octopus_merge_base(oct_merge_base, curr_head, merge_head, fork_point))
-               if (!is_null_sha1(fork_point) && !hashcmp(oct_merge_base, fork_point))
+       if (!get_octopus_merge_base(&oct_merge_base, curr_head, merge_head, fork_point))
+               if (!is_null_oid(fork_point) && !oidcmp(&oct_merge_base, fork_point))
                        fork_point = NULL;
 
        argv_array_push(&args, "rebase");
@@ -754,12 +754,12 @@ static int run_rebase(const unsigned char *curr_head,
                warning(_("ignoring --verify-signatures for rebase"));
 
        argv_array_push(&args, "--onto");
-       argv_array_push(&args, sha1_to_hex(merge_head));
+       argv_array_push(&args, oid_to_hex(merge_head));
 
-       if (fork_point && !is_null_sha1(fork_point))
-               argv_array_push(&args, sha1_to_hex(fork_point));
+       if (fork_point && !is_null_oid(fork_point))
+               argv_array_push(&args, oid_to_hex(fork_point));
        else
-               argv_array_push(&args, sha1_to_hex(merge_head));
+               argv_array_push(&args, oid_to_hex(merge_head));
 
        ret = run_command_v_opt(args.argv, RUN_GIT_CMD);
        argv_array_clear(&args);
@@ -769,9 +769,9 @@ static int run_rebase(const unsigned char *curr_head,
 int cmd_pull(int argc, const char **argv, const char *prefix)
 {
        const char *repo, **refspecs;
-       struct sha1_array merge_heads = SHA1_ARRAY_INIT;
-       unsigned char orig_head[GIT_SHA1_RAWSZ], curr_head[GIT_SHA1_RAWSZ];
-       unsigned char rebase_fork_point[GIT_SHA1_RAWSZ];
+       struct oid_array merge_heads = OID_ARRAY_INIT;
+       struct object_id orig_head, curr_head;
+       struct object_id rebase_fork_point;
 
        if (!getenv("GIT_REFLOG_ACTION"))
                set_reflog_message(argc, argv);
@@ -794,8 +794,8 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (file_exists(git_path("MERGE_HEAD")))
                die_conclude_merge();
 
-       if (get_sha1("HEAD", orig_head))
-               hashclr(orig_head);
+       if (get_oid("HEAD", &orig_head))
+               oidclr(&orig_head);
 
        if (!opt_rebase && opt_autostash != -1)
                die(_("--[no-]autostash option is only valid with --rebase."));
@@ -805,15 +805,15 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
                if (opt_autostash != -1)
                        autostash = opt_autostash;
 
-               if (is_null_sha1(orig_head) && !is_cache_unborn())
+               if (is_null_oid(&orig_head) && !is_cache_unborn())
                        die(_("Updating an unborn branch with changes added to the index."));
 
                if (!autostash)
                        require_clean_work_tree(N_("pull with rebase"),
                                _("please commit or stash them."), 1, 0);
 
-               if (get_rebase_fork_point(rebase_fork_point, repo, *refspecs))
-                       hashclr(rebase_fork_point);
+               if (get_rebase_fork_point(&rebase_fork_point, repo, *refspecs))
+                       oidclr(&rebase_fork_point);
        }
 
        if (run_fetch(repo, refspecs))
@@ -822,11 +822,11 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (opt_dry_run)
                return 0;
 
-       if (get_sha1("HEAD", curr_head))
-               hashclr(curr_head);
+       if (get_oid("HEAD", &curr_head))
+               oidclr(&curr_head);
 
-       if (!is_null_sha1(orig_head) && !is_null_sha1(curr_head) &&
-                       hashcmp(orig_head, curr_head)) {
+       if (!is_null_oid(&orig_head) && !is_null_oid(&curr_head) &&
+                       oidcmp(&orig_head, &curr_head)) {
                /*
                 * The fetch involved updating the current branch.
                 *
@@ -837,15 +837,15 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
 
                warning(_("fetch updated the current branch head.\n"
                        "fast-forwarding your working tree from\n"
-                       "commit %s."), sha1_to_hex(orig_head));
+                       "commit %s."), oid_to_hex(&orig_head));
 
-               if (checkout_fast_forward(orig_head, curr_head, 0))
+               if (checkout_fast_forward(orig_head.hash, curr_head.hash, 0))
                        die(_("Cannot fast-forward your working tree.\n"
                                "After making sure that you saved anything precious from\n"
                                "$ git diff %s\n"
                                "output, run\n"
                                "$ git reset --hard\n"
-                               "to recover."), sha1_to_hex(orig_head));
+                               "to recover."), oid_to_hex(&orig_head));
        }
 
        get_merge_heads(&merge_heads);
@@ -853,10 +853,10 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (!merge_heads.nr)
                die_no_merge_candidates(repo, refspecs);
 
-       if (is_null_sha1(orig_head)) {
+       if (is_null_oid(&orig_head)) {
                if (merge_heads.nr > 1)
                        die(_("Cannot merge multiple branches into empty head."));
-               return pull_into_void(*merge_heads.sha1, curr_head);
+               return pull_into_void(merge_heads.oid, &curr_head);
        }
        if (opt_rebase && merge_heads.nr > 1)
                die(_("Cannot rebase onto multiple branches."));
@@ -865,15 +865,15 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
                struct commit_list *list = NULL;
                struct commit *merge_head, *head;
 
-               head = lookup_commit_reference(orig_head);
+               head = lookup_commit_reference(orig_head.hash);
                commit_list_insert(head, &list);
-               merge_head = lookup_commit_reference(merge_heads.sha1[0]);
+               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();
                }
-               return run_rebase(curr_head, *merge_heads.sha1, rebase_fork_point);
+               return run_rebase(&curr_head, merge_heads.oid, &rebase_fork_point);
        } else {
                return run_merge();
        }
index 5c22e9f2e56b7d8049890e44f4c6fb63b16f8357..a597759d8fac205f41c406d0a0e32f9240941190 100644 (file)
@@ -510,8 +510,8 @@ int cmd_push(int argc, const char **argv, const char *prefix)
        int push_cert = -1;
        int rc;
        const char *repo = NULL;        /* default repository */
-       static struct string_list push_options = STRING_LIST_INIT_DUP;
-       static struct string_list_item *item;
+       struct string_list push_options = STRING_LIST_INIT_DUP;
+       const struct string_list_item *item;
 
        struct option options[] = {
                OPT__VERBOSITY(&verbosity),
@@ -584,6 +584,7 @@ int cmd_push(int argc, const char **argv, const char *prefix)
                        die(_("push options must not have new line characters"));
 
        rc = do_push(repo, flags, &push_options);
+       string_list_clear(&push_options, 0);
        if (rc == -1)
                usage_with_options(push_usage, options);
        else
index 8ba64bc785670590897638c8e58172c74556189d..23e212ee8c5b2d26f03ac6be2b822a87ad06c233 100644 (file)
 #include "builtin.h"
 #include "parse-options.h"
 #include "resolve-undo.h"
+#include "submodule.h"
+#include "submodule-config.h"
 
 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)
 {
@@ -96,6 +99,23 @@ 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)
+{
+       if (unset) {
+               recurse_submodules = RECURSE_SUBMODULES_OFF;
+               return 0;
+       }
+       if (arg)
+               recurse_submodules =
+                       parse_update_recurse_submodules_arg(opt->long_name,
+                                                           arg);
+       else
+               recurse_submodules = RECURSE_SUBMODULES_ON;
+
+       return 0;
+}
+
 static struct lock_file lock_file;
 
 int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
@@ -137,6 +157,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,
+                           "checkout", "control recursive updating of submodules",
+                           PARSE_OPT_OPTARG, option_parse_recurse_submodules },
                OPT_END()
        };
 
@@ -152,6 +175,12 @@ int cmd_read_tree(int argc, const char **argv, const char *unused_prefix)
 
        hold_locked_index(&lock_file, LOCK_DIE_ON_ERROR);
 
+       if (recurse_submodules != RECURSE_SUBMODULES_DEFAULT) {
+               gitmodules_config();
+               git_config(submodule_config, NULL);
+               set_config_update_recurse_submodules(RECURSE_SUBMODULES_ON);
+       }
+
        prefix_set = opts.prefix ? 1 : 0;
        if (1 < opts.merge + opts.reset + prefix_set)
                die("Which one? -m, --reset, or --prefix?");
index 83492af05f2bfd8ca1ec796b7ae8dccc84f0f31b..f96834f42c9849746b64c20c84869232cbac7367 100644 (file)
@@ -225,10 +225,10 @@ static int receive_pack_config(const char *var, const char *value, void *cb)
        return git_default_config(var, value, cb);
 }
 
-static void show_ref(const char *path, const unsigned char *sha1)
+static void show_ref(const char *path, const struct object_id *oid)
 {
        if (sent_capabilities) {
-               packet_write_fmt(1, "%s %s\n", sha1_to_hex(sha1), path);
+               packet_write_fmt(1, "%s %s\n", oid_to_hex(oid), path);
        } else {
                struct strbuf cap = STRBUF_INIT;
 
@@ -244,7 +244,7 @@ static void show_ref(const char *path, const unsigned char *sha1)
                        strbuf_addstr(&cap, " push-options");
                strbuf_addf(&cap, " agent=%s", git_user_agent_sanitized());
                packet_write_fmt(1, "%s %s%c%s\n",
-                            sha1_to_hex(sha1), path, 0, cap.buf);
+                            oid_to_hex(oid), path, 0, cap.buf);
                strbuf_release(&cap);
                sent_capabilities = 1;
        }
@@ -271,7 +271,7 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
        } else {
                oidset_insert(seen, oid);
        }
-       show_ref(path, oid->hash);
+       show_ref(path, oid);
        return 0;
 }
 
@@ -284,7 +284,7 @@ static void show_one_alternate_ref(const char *refname,
        if (oidset_insert(seen, oid))
                return;
 
-       show_ref(".have", oid->hash);
+       show_ref(".have", oid);
 }
 
 static void write_head_info(void)
@@ -295,7 +295,7 @@ static void write_head_info(void)
        for_each_alternate_ref(show_one_alternate_ref, &seen);
        oidset_clear(&seen);
        if (!sent_capabilities)
-               show_ref("capabilities^{}", null_sha1);
+               show_ref("capabilities^{}", &null_oid);
 
        advertise_shallow_grafts(1);
 
@@ -309,8 +309,8 @@ struct command {
        unsigned int skip_update:1,
                     did_not_exist:1;
        int index;
-       unsigned char old_sha1[20];
-       unsigned char new_sha1[20];
+       struct object_id old_oid;
+       struct object_id new_oid;
        char ref_name[FLEX_ARRAY]; /* more */
 };
 
@@ -723,7 +723,7 @@ static int feed_receive_hook(void *state_, const char **bufp, size_t *sizep)
                return -1; /* EOF */
        strbuf_reset(&state->buf);
        strbuf_addf(&state->buf, "%s %s %s\n",
-                   sha1_to_hex(cmd->old_sha1), sha1_to_hex(cmd->new_sha1),
+                   oid_to_hex(&cmd->old_oid), oid_to_hex(&cmd->new_oid),
                    cmd->ref_name);
        state->cmd = cmd->next;
        if (bufp) {
@@ -764,15 +764,14 @@ static int run_update_hook(struct command *cmd)
                return 0;
 
        argv[1] = cmd->ref_name;
-       argv[2] = sha1_to_hex(cmd->old_sha1);
-       argv[3] = sha1_to_hex(cmd->new_sha1);
+       argv[2] = oid_to_hex(&cmd->old_oid);
+       argv[3] = oid_to_hex(&cmd->new_oid);
        argv[4] = NULL;
 
        proc.no_stdin = 1;
        proc.stdout_to_stderr = 1;
        proc.err = use_sideband ? -1 : 0;
        proc.argv = argv;
-       proc.env = tmp_objdir_env(tmp_objdir);
 
        code = start_command(&proc);
        if (code)
@@ -831,7 +830,7 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20]);
 static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
 {
        static struct lock_file shallow_lock;
-       struct sha1_array extra = SHA1_ARRAY_INIT;
+       struct oid_array extra = OID_ARRAY_INIT;
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        uint32_t mask = 1 << (cmd->index % 32);
        int i;
@@ -842,13 +841,13 @@ static int update_shallow_ref(struct command *cmd, struct shallow_info *si)
                if (si->used_shallow[i] &&
                    (si->used_shallow[i][cmd->index / 32] & mask) &&
                    !delayed_reachability_test(si, i))
-                       sha1_array_append(&extra, si->shallow->sha1[i]);
+                       oid_array_append(&extra, &si->shallow->oid[i]);
 
        opt.env = tmp_objdir_env(tmp_objdir);
        setup_alternate_shallow(&shallow_lock, &opt.shallow_file, &extra);
        if (check_connected(command_singleton_iterator, cmd, &opt)) {
                rollback_lock_file(&shallow_lock);
-               sha1_array_clear(&extra);
+               oid_array_clear(&extra);
                return -1;
        }
 
@@ -859,10 +858,10 @@ 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.sha1[i]);
+               register_shallow(extra.oid[i].hash);
 
        si->shallow_ref[cmd->index] = 0;
-       sha1_array_clear(&extra);
+       oid_array_clear(&extra);
        return 0;
 }
 
@@ -988,8 +987,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;
-       unsigned char *old_sha1 = cmd->old_sha1;
-       unsigned char *new_sha1 = cmd->new_sha1;
+       struct object_id *old_oid = &cmd->old_oid;
+       struct object_id *new_oid = &cmd->new_oid;
 
        /* only refs/... are allowed */
        if (!starts_with(name, "refs/") || check_refname_format(name + 5, 0)) {
@@ -1014,20 +1013,20 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                                refuse_unconfigured_deny();
                        return "branch is currently checked out";
                case DENY_UPDATE_INSTEAD:
-                       ret = update_worktree(new_sha1);
+                       ret = update_worktree(new_oid->hash);
                        if (ret)
                                return ret;
                        break;
                }
        }
 
-       if (!is_null_sha1(new_sha1) && !has_sha1_file(new_sha1)) {
+       if (!is_null_oid(new_oid) && !has_object_file(new_oid)) {
                error("unpack should have generated %s, "
-                     "but I can't find it!", sha1_to_hex(new_sha1));
+                     "but I can't find it!", oid_to_hex(new_oid));
                return "bad pack";
        }
 
-       if (!is_null_sha1(old_sha1) && is_null_sha1(new_sha1)) {
+       if (!is_null_oid(old_oid) && is_null_oid(new_oid)) {
                if (deny_deletes && starts_with(name, "refs/heads/")) {
                        rp_error("denying ref deletion for %s", name);
                        return "deletion prohibited";
@@ -1053,14 +1052,14 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                }
        }
 
-       if (deny_non_fast_forwards && !is_null_sha1(new_sha1) &&
-           !is_null_sha1(old_sha1) &&
+       if (deny_non_fast_forwards && !is_null_oid(new_oid) &&
+           !is_null_oid(old_oid) &&
            starts_with(name, "refs/heads/")) {
                struct object *old_object, *new_object;
                struct commit *old_commit, *new_commit;
 
-               old_object = parse_object(old_sha1);
-               new_object = parse_object(new_sha1);
+               old_object = parse_object(old_oid->hash);
+               new_object = parse_object(new_oid->hash);
 
                if (!old_object || !new_object ||
                    old_object->type != OBJ_COMMIT ||
@@ -1081,10 +1080,10 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                return "hook declined";
        }
 
-       if (is_null_sha1(new_sha1)) {
+       if (is_null_oid(new_oid)) {
                struct strbuf err = STRBUF_INIT;
-               if (!parse_object(old_sha1)) {
-                       old_sha1 = NULL;
+               if (!parse_object(old_oid->hash)) {
+                       old_oid = NULL;
                        if (ref_exists(name)) {
                                rp_warning("Allowing deletion of corrupt ref.");
                        } else {
@@ -1094,7 +1093,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
                }
                if (ref_transaction_delete(transaction,
                                           namespaced_name,
-                                          old_sha1,
+                                          old_oid->hash,
                                           0, "push", &err)) {
                        rp_error("%s", err.buf);
                        strbuf_release(&err);
@@ -1111,7 +1110,7 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 
                if (ref_transaction_update(transaction,
                                           namespaced_name,
-                                          new_sha1, old_sha1,
+                                          new_oid->hash, old_oid->hash,
                                           0, "push",
                                           &err)) {
                        rp_error("%s", err.buf);
@@ -1128,25 +1127,22 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 static void run_update_post_hook(struct command *commands)
 {
        struct command *cmd;
-       int argc;
        struct child_process proc = CHILD_PROCESS_INIT;
        const char *hook;
 
        hook = find_hook("post-update");
-       for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
-               if (cmd->error_string || cmd->did_not_exist)
-                       continue;
-               argc++;
-       }
-       if (!argc || !hook)
+       if (!hook)
                return;
 
-       argv_array_push(&proc.args, hook);
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (cmd->error_string || cmd->did_not_exist)
                        continue;
+               if (!proc.args.argc)
+                       argv_array_push(&proc.args, hook);
                argv_array_push(&proc.args, cmd->ref_name);
        }
+       if (!proc.args.argc)
+               return;
 
        proc.no_stdin = 1;
        proc.stdout_to_stderr = 1;
@@ -1165,7 +1161,7 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
        const char *dst_name;
        struct string_list_item *item;
        struct command *dst_cmd;
-       unsigned char sha1[GIT_SHA1_RAWSZ];
+       unsigned char sha1[GIT_MAX_RAWSZ];
        int flag;
 
        strbuf_addf(&buf, "%s%s", get_git_namespace(), cmd->ref_name);
@@ -1190,8 +1186,8 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
 
        dst_cmd = (struct command *) item->util;
 
-       if (!hashcmp(cmd->old_sha1, dst_cmd->old_sha1) &&
-           !hashcmp(cmd->new_sha1, dst_cmd->new_sha1))
+       if (!oidcmp(&cmd->old_oid, &dst_cmd->old_oid) &&
+           !oidcmp(&cmd->new_oid, &dst_cmd->new_oid))
                return;
 
        dst_cmd->skip_update = 1;
@@ -1199,11 +1195,11 @@ static void check_aliased_update(struct command *cmd, struct string_list *list)
        rp_error("refusing inconsistent update between symref '%s' (%s..%s) and"
                 " its target '%s' (%s..%s)",
                 cmd->ref_name,
-                find_unique_abbrev(cmd->old_sha1, DEFAULT_ABBREV),
-                find_unique_abbrev(cmd->new_sha1, DEFAULT_ABBREV),
+                find_unique_abbrev(cmd->old_oid.hash, DEFAULT_ABBREV),
+                find_unique_abbrev(cmd->new_oid.hash, DEFAULT_ABBREV),
                 dst_cmd->ref_name,
-                find_unique_abbrev(dst_cmd->old_sha1, DEFAULT_ABBREV),
-                find_unique_abbrev(dst_cmd->new_sha1, DEFAULT_ABBREV));
+                find_unique_abbrev(dst_cmd->old_oid.hash, DEFAULT_ABBREV),
+                find_unique_abbrev(dst_cmd->new_oid.hash, DEFAULT_ABBREV));
 
        cmd->error_string = dst_cmd->error_string =
                "inconsistent aliased update";
@@ -1234,10 +1230,10 @@ static int command_singleton_iterator(void *cb_data, unsigned char sha1[20])
        struct command **cmd_list = cb_data;
        struct command *cmd = *cmd_list;
 
-       if (!cmd || is_null_sha1(cmd->new_sha1))
+       if (!cmd || is_null_oid(&cmd->new_oid))
                return -1; /* end of list */
        *cmd_list = NULL; /* this returns only one */
-       hashcpy(sha1, cmd->new_sha1);
+       hashcpy(sha1, cmd->new_oid.hash);
        return 0;
 }
 
@@ -1278,8 +1274,8 @@ static int iterate_receive_command_list(void *cb_data, unsigned char sha1[20])
                if (shallow_update && data->si->shallow_ref[cmd->index])
                        /* to be checked in update_shallow_ref() */
                        continue;
-               if (!is_null_sha1(cmd->new_sha1) && !cmd->skip_update) {
-                       hashcpy(sha1, cmd->new_sha1);
+               if (!is_null_oid(&cmd->new_oid) && !cmd->skip_update) {
+                       hashcpy(sha1, cmd->new_oid.hash);
                        *cmd_list = cmd->next;
                        return 0;
                }
@@ -1306,7 +1302,7 @@ static void reject_updates_to_hidden(struct command *commands)
 
                if (!ref_is_hidden(cmd->ref_name, refname_full.buf))
                        continue;
-               if (is_null_sha1(cmd->new_sha1))
+               if (is_null_oid(&cmd->new_oid))
                        cmd->error_string = "deny deleting a hidden ref";
                else
                        cmd->error_string = "deny updating a hidden ref";
@@ -1489,23 +1485,23 @@ static struct command **queue_command(struct command **tail,
                                      const char *line,
                                      int linelen)
 {
-       unsigned char old_sha1[20], new_sha1[20];
+       struct object_id old_oid, new_oid;
        struct command *cmd;
        const char *refname;
        int reflen;
+       const char *p;
 
-       if (linelen < 83 ||
-           line[40] != ' ' ||
-           line[81] != ' ' ||
-           get_sha1_hex(line, old_sha1) ||
-           get_sha1_hex(line + 41, new_sha1))
+       if (parse_oid_hex(line, &old_oid, &p) ||
+           *p++ != ' ' ||
+           parse_oid_hex(p, &new_oid, &p) ||
+           *p++ != ' ')
                die("protocol error: expected old/new/ref, got '%s'", line);
 
-       refname = line + 82;
-       reflen = linelen - 82;
+       refname = p;
+       reflen = linelen - (p - line);
        FLEX_ALLOC_MEM(cmd, ref_name, refname, reflen);
-       hashcpy(cmd->old_sha1, old_sha1);
-       hashcpy(cmd->new_sha1, new_sha1);
+       oidcpy(&cmd->old_oid, &old_oid);
+       oidcpy(&cmd->new_oid, &new_oid);
        *tail = cmd;
        return &cmd->next;
 }
@@ -1527,12 +1523,12 @@ static void queue_commands_from_cert(struct command **tail,
 
        while (boc < eoc) {
                const char *eol = memchr(boc, '\n', eoc - boc);
-               tail = queue_command(tail, boc, eol ? eol - boc : eoc - eol);
+               tail = queue_command(tail, boc, eol ? eol - boc : eoc - boc);
                boc = eol ? eol + 1 : eoc;
        }
 }
 
-static struct command *read_head_info(struct sha1_array *shallow)
+static struct command *read_head_info(struct oid_array *shallow)
 {
        struct command *commands = NULL;
        struct command **p = &commands;
@@ -1544,12 +1540,12 @@ static struct command *read_head_info(struct sha1_array *shallow)
                if (!line)
                        break;
 
-               if (len == 48 && starts_with(line, "shallow ")) {
-                       unsigned char sha1[20];
-                       if (get_sha1_hex(line + 8, sha1))
+               if (len 8 && starts_with(line, "shallow ")) {
+                       struct object_id oid;
+                       if (get_oid_hex(line + 8, &oid))
                                die("protocol error: expected shallow sha, got '%s'",
                                    line + 8);
-                       sha1_array_append(shallow, sha1);
+                       oid_array_append(shallow, &oid);
                        continue;
                }
 
@@ -1637,12 +1633,17 @@ static const char *parse_pack_header(struct pack_header *hdr)
 
 static const char *pack_lockfile;
 
+static void push_header_arg(struct argv_array *args, struct pack_header *hdr)
+{
+       argv_array_pushf(args, "--pack_header=%"PRIu32",%"PRIu32,
+                       ntohl(hdr->hdr_version), ntohl(hdr->hdr_entries));
+}
+
 static const char *unpack(int err_fd, struct shallow_info *si)
 {
        struct pack_header hdr;
        const char *hdr_err;
        int status;
-       char hdr_arg[38];
        struct child_process child = CHILD_PROCESS_INIT;
        int fsck_objects = (receive_fsck_objects >= 0
                            ? receive_fsck_objects
@@ -1656,9 +1657,6 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                        close(err_fd);
                return hdr_err;
        }
-       snprintf(hdr_arg, sizeof(hdr_arg),
-                       "--pack_header=%"PRIu32",%"PRIu32,
-                       ntohl(hdr.hdr_version), ntohl(hdr.hdr_entries));
 
        if (si->nr_ours || si->nr_theirs) {
                alt_shallow_file = setup_temporary_shallow(si->shallow);
@@ -1682,7 +1680,8 @@ static const char *unpack(int err_fd, struct shallow_info *si)
        tmp_objdir_add_as_alternate(tmp_objdir);
 
        if (ntohl(hdr.hdr_entries) < unpack_limit) {
-               argv_array_pushl(&child.args, "unpack-objects", hdr_arg, NULL);
+               argv_array_push(&child.args, "unpack-objects");
+               push_header_arg(&child.args, &hdr);
                if (quiet)
                        argv_array_push(&child.args, "-q");
                if (fsck_objects)
@@ -1698,12 +1697,12 @@ static const char *unpack(int err_fd, struct shallow_info *si)
                if (status)
                        return "unpack-objects abnormal exit";
        } else {
-               char hostname[256];
+               char hostname[HOST_NAME_MAX + 1];
 
-               argv_array_pushl(&child.args, "index-pack",
-                                "--stdin", hdr_arg, NULL);
+               argv_array_pushl(&child.args, "index-pack", "--stdin", NULL);
+               push_header_arg(&child.args, &hdr);
 
-               if (gethostname(hostname, sizeof(hostname)))
+               if (xgethostname(hostname, sizeof(hostname)))
                        xsnprintf(hostname, sizeof(hostname), "localhost");
                argv_array_pushf(&child.args,
                                 "--keep=receive-pack %"PRIuMAX" on %s",
@@ -1807,7 +1806,7 @@ static void prepare_shallow_update(struct command *commands,
 
 static void update_shallow_info(struct command *commands,
                                struct shallow_info *si,
-                               struct sha1_array *ref)
+                               struct oid_array *ref)
 {
        struct command *cmd;
        int *ref_status;
@@ -1818,9 +1817,9 @@ static void update_shallow_info(struct command *commands,
        }
 
        for (cmd = commands; cmd; cmd = cmd->next) {
-               if (is_null_sha1(cmd->new_sha1))
+               if (is_null_oid(&cmd->new_oid))
                        continue;
-               sha1_array_append(ref, cmd->new_sha1);
+               oid_array_append(ref, &cmd->new_oid);
                cmd->index = ref->nr - 1;
        }
        si->ref = ref;
@@ -1833,7 +1832,7 @@ static void update_shallow_info(struct command *commands,
        ALLOC_ARRAY(ref_status, ref->nr);
        assign_shallow_commits_to_refs(si, NULL, ref_status);
        for (cmd = commands; cmd; cmd = cmd->next) {
-               if (is_null_sha1(cmd->new_sha1))
+               if (is_null_oid(&cmd->new_oid))
                        continue;
                if (ref_status[cmd->index]) {
                        cmd->error_string = "shallow update not allowed";
@@ -1871,7 +1870,7 @@ static int delete_only(struct command *commands)
 {
        struct command *cmd;
        for (cmd = commands; cmd; cmd = cmd->next) {
-               if (!is_null_sha1(cmd->new_sha1))
+               if (!is_null_oid(&cmd->new_oid))
                        return 0;
        }
        return 1;
@@ -1881,8 +1880,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
 {
        int advertise_refs = 0;
        struct command *commands;
-       struct sha1_array shallow = SHA1_ARRAY_INIT;
-       struct sha1_array ref = SHA1_ARRAY_INIT;
+       struct oid_array shallow = OID_ARRAY_INIT;
+       struct oid_array ref = OID_ARRAY_INIT;
        struct shallow_info si;
 
        struct option options[] = {
@@ -1974,8 +1973,8 @@ int cmd_receive_pack(int argc, const char **argv, const char *prefix)
        }
        if (use_sideband)
                packet_flush(1);
-       sha1_array_clear(&shallow);
-       sha1_array_clear(&ref);
+       oid_array_clear(&shallow);
+       oid_array_clear(&ref);
        free((void *)push_cert_nonce);
        return 0;
 }
index f83e7b8fc1758aa837b4dd48708a3d6c31bcd226..ab17668f4330a5e688d423a60c138768b9955e1b 100644 (file)
@@ -93,28 +93,34 @@ typedef int (*each_replace_name_fn)(const char *name, const char *ref,
 static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
 {
        const char **p, *full_hex;
-       char ref[PATH_MAX];
+       struct strbuf ref = STRBUF_INIT;
+       size_t base_len;
        int had_error = 0;
        struct object_id oid;
 
+       strbuf_addstr(&ref, git_replace_ref_base);
+       base_len = ref.len;
+
        for (p = argv; *p; p++) {
                if (get_oid(*p, &oid)) {
                        error("Failed to resolve '%s' as a valid ref.", *p);
                        had_error = 1;
                        continue;
                }
-               full_hex = oid_to_hex(&oid);
-               snprintf(ref, sizeof(ref), "%s%s", git_replace_ref_base, full_hex);
-               /* read_ref() may reuse the buffer */
-               full_hex = ref + strlen(git_replace_ref_base);
-               if (read_ref(ref, oid.hash)) {
+
+               strbuf_setlen(&ref, base_len);
+               strbuf_addstr(&ref, oid_to_hex(&oid));
+               full_hex = ref.buf + base_len;
+
+               if (read_ref(ref.buf, oid.hash)) {
                        error("replace ref '%s' not found.", full_hex);
                        had_error = 1;
                        continue;
                }
-               if (fn(full_hex, ref, &oid))
+               if (fn(full_hex, ref.buf, &oid))
                        had_error = 1;
        }
+       strbuf_release(&ref);
        return had_error;
 }
 
@@ -129,21 +135,18 @@ static int delete_replace_ref(const char *name, const char *ref,
 
 static void check_ref_valid(struct object_id *object,
                            struct object_id *prev,
-                           char *ref,
-                           int ref_size,
+                           struct strbuf *ref,
                            int force)
 {
-       if (snprintf(ref, ref_size,
-                    "%s%s", git_replace_ref_base,
-                    oid_to_hex(object)) > ref_size - 1)
-               die("replace ref name too long: %.*s...", 50, ref);
-       if (check_refname_format(ref, 0))
-               die("'%s' is not a valid ref name.", ref);
-
-       if (read_ref(ref, prev->hash))
+       strbuf_reset(ref);
+       strbuf_addf(ref, "%s%s", git_replace_ref_base, oid_to_hex(object));
+       if (check_refname_format(ref->buf, 0))
+               die("'%s' is not a valid ref name.", ref->buf);
+
+       if (read_ref(ref->buf, prev->hash))
                oidclr(prev);
        else if (!force)
-               die("replace ref '%s' already exists", ref);
+               die("replace ref '%s' already exists", ref->buf);
 }
 
 static int replace_object_oid(const char *object_ref,
@@ -154,7 +157,7 @@ static int replace_object_oid(const char *object_ref,
 {
        struct object_id prev;
        enum object_type obj_type, repl_type;
-       char ref[PATH_MAX];
+       struct strbuf ref = STRBUF_INIT;
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
@@ -167,16 +170,17 @@ static int replace_object_oid(const char *object_ref,
                    object_ref, typename(obj_type),
                    replace_ref, typename(repl_type));
 
-       check_ref_valid(object, &prev, ref, sizeof(ref), force);
+       check_ref_valid(object, &prev, &ref, force);
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, ref, repl->hash, prev.hash,
+           ref_transaction_update(transaction, ref.buf, repl->hash, prev.hash,
                                   0, NULL, &err) ||
            ref_transaction_commit(transaction, &err))
                die("%s", err.buf);
 
        ref_transaction_free(transaction);
+       strbuf_release(&ref);
        return 0;
 }
 
@@ -280,7 +284,7 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
        char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
        enum object_type type;
        struct object_id old, new, prev;
-       char ref[PATH_MAX];
+       struct strbuf ref = STRBUF_INIT;
 
        if (get_oid(object_ref, &old) < 0)
                die("Not a valid object name: '%s'", object_ref);
@@ -289,7 +293,8 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
        if (type < 0)
                die("unable to get object type for %s", oid_to_hex(&old));
 
-       check_ref_valid(&old, &prev, ref, sizeof(ref), force);
+       check_ref_valid(&old, &prev, &ref, force);
+       strbuf_release(&ref);
 
        export_object(&old, type, raw, tmpfile);
        if (launch_editor(tmpfile, NULL, NULL) < 0)
index 0aa93d58919742852b05b952e8e35b5270f74ba8..bcf77f0b8a201278f0887933e568937473eb7251 100644 (file)
@@ -212,7 +212,7 @@ static void print_var_int(const char *var, int val)
 static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
 {
        int cnt, flags = info->flags;
-       char hex[GIT_SHA1_HEXSZ + 1] = "";
+       char hex[GIT_MAX_HEXSZ + 1] = "";
        struct commit_list *tried;
        struct rev_info *revs = info->revs;
 
index 2549643267440c5742690e3b808943de6fad19af..051333091062ef8e2e717ec4aef8a9bdf3b4450f 100644 (file)
@@ -205,21 +205,22 @@ static int anti_reference(const char *refname, const struct object_id *oid, int
        return 0;
 }
 
-static int show_abbrev(const unsigned char *sha1, void *cb_data)
+static int show_abbrev(const struct object_id *oid, void *cb_data)
 {
-       show_rev(NORMAL, sha1, NULL);
+       show_rev(NORMAL, oid->hash, NULL);
        return 0;
 }
 
 static void show_datestring(const char *flag, const char *datestr)
 {
-       static char buffer[100];
+       char *buffer;
 
        /* date handling requires both flags and revs */
        if ((filter & (DO_FLAGS | DO_REVS)) != (DO_FLAGS | DO_REVS))
                return;
-       snprintf(buffer, sizeof(buffer), "%s%lu", flag, approxidate(datestr));
+       buffer = xstrfmt("%s%lu", flag, approxidate(datestr));
        show(buffer);
+       free(buffer);
 }
 
 static int show_file(const char *arg, int output_prefix)
@@ -228,9 +229,9 @@ static int show_file(const char *arg, int output_prefix)
        if ((filter & (DO_NONFLAGS|DO_NOREV)) == (DO_NONFLAGS|DO_NOREV)) {
                if (output_prefix) {
                        const char *prefix = startup_info->prefix;
-                       show(prefix_filename(prefix,
-                                            prefix ? strlen(prefix) : 0,
-                                            arg));
+                       char *fname = prefix_filename(prefix, arg);
+                       show(fname);
+                       free(fname);
                } else
                        show(arg);
                return 1;
@@ -536,6 +537,34 @@ N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
    "\n"
    "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
 
+/*
+ * Parse "opt" or "opt=<value>", setting value respectively to either
+ * NULL or the string after "=".
+ */
+static int opt_with_value(const char *arg, const char *opt, const char **value)
+{
+       if (skip_prefix(arg, opt, &arg)) {
+               if (!*arg) {
+                       *value = NULL;
+                       return 1;
+               }
+               if (*arg++ == '=') {
+                       *value = arg;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static void handle_ref_opt(const char *pattern, const char *prefix)
+{
+       if (pattern)
+               for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+       else
+               for_each_ref_in(prefix, show_reference, NULL);
+       clear_ref_exclusion(&ref_excludes);
+}
+
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
@@ -675,14 +704,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                flags |= GET_SHA1_QUIETLY;
                                continue;
                        }
-                       if (!strcmp(arg, "--short") ||
-                           starts_with(arg, "--short=")) {
+                       if (opt_with_value(arg, "--short", &arg)) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
                                abbrev = DEFAULT_ABBREV;
-                               if (!arg[7])
+                               if (!arg)
                                        continue;
-                               abbrev = strtoul(arg + 8, NULL, 10);
+                               abbrev = strtoul(arg, NULL, 10);
                                if (abbrev < MINIMUM_ABBREV)
                                        abbrev = MINIMUM_ABBREV;
                                else if (40 <= abbrev)
@@ -705,17 +733,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                symbolic = SHOW_SYMBOLIC_FULL;
                                continue;
                        }
-                       if (starts_with(arg, "--abbrev-ref") &&
-                           (!arg[12] || arg[12] == '=')) {
+                       if (opt_with_value(arg, "--abbrev-ref", &arg)) {
                                abbrev_ref = 1;
                                abbrev_ref_strict = warn_ambiguous_refs;
-                               if (arg[12] == '=') {
-                                       if (!strcmp(arg + 13, "strict"))
+                               if (arg) {
+                                       if (!strcmp(arg, "strict"))
                                                abbrev_ref_strict = 1;
-                                       else if (!strcmp(arg + 13, "loose"))
+                                       else if (!strcmp(arg, "loose"))
                                                abbrev_ref_strict = 0;
                                        else
-                                               die("unknown mode for %s", arg);
+                                               die("unknown mode for --abbrev-ref: %s",
+                                                   arg);
                                }
                                continue;
                        }
@@ -723,8 +751,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
-                       if (starts_with(arg, "--disambiguate=")) {
-                               for_each_abbrev(arg + 15, show_abbrev, NULL);
+                       if (skip_prefix(arg, "--disambiguate=", &arg)) {
+                               for_each_abbrev(arg, show_abbrev, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
@@ -732,46 +760,24 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                                continue;
                        }
-                       if (starts_with(arg, "--branches=")) {
-                               for_each_glob_ref_in(show_reference, arg + 11,
-                                       "refs/heads/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (!strcmp(arg, "--branches")) {
-                               for_each_branch_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (starts_with(arg, "--tags=")) {
-                               for_each_glob_ref_in(show_reference, arg + 7,
-                                       "refs/tags/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (!strcmp(arg, "--tags")) {
-                               for_each_tag_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--branches", &arg)) {
+                               handle_ref_opt(arg, "refs/heads/");
                                continue;
                        }
-                       if (starts_with(arg, "--glob=")) {
-                               for_each_glob_ref(show_reference, arg + 7, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--tags", &arg)) {
+                               handle_ref_opt(arg, "refs/tags/");
                                continue;
                        }
-                       if (starts_with(arg, "--remotes=")) {
-                               for_each_glob_ref_in(show_reference, arg + 10,
-                                       "refs/remotes/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (skip_prefix(arg, "--glob=", &arg)) {
+                               handle_ref_opt(arg, NULL);
                                continue;
                        }
-                       if (!strcmp(arg, "--remotes")) {
-                               for_each_remote_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--remotes", &arg)) {
+                               handle_ref_opt(arg, "refs/remotes/");
                                continue;
                        }
-                       if (starts_with(arg, "--exclude=")) {
-                               add_ref_exclusion(&ref_excludes, arg + 10);
+                       if (skip_prefix(arg, "--exclude=", &arg)) {
+                               add_ref_exclusion(&ref_excludes, arg);
                                continue;
                        }
                        if (!strcmp(arg, "--show-toplevel")) {
@@ -872,20 +878,20 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                }
                                continue;
                        }
-                       if (starts_with(arg, "--since=")) {
-                               show_datestring("--max-age=", arg+8);
+                       if (skip_prefix(arg, "--since=", &arg)) {
+                               show_datestring("--max-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--after=")) {
-                               show_datestring("--max-age=", arg+8);
+                       if (skip_prefix(arg, "--after=", &arg)) {
+                               show_datestring("--max-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--before=")) {
-                               show_datestring("--min-age=", arg+9);
+                       if (skip_prefix(arg, "--before=", &arg)) {
+                               show_datestring("--min-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--until=")) {
-                               show_datestring("--min-age=", arg+8);
+                       if (skip_prefix(arg, "--until=", &arg)) {
+                               show_datestring("--min-age=", arg);
                                continue;
                        }
                        if (show_flag(arg) && verify)
index 4ca5b515449a355ae04f0ff496a8ee0b7143e29a..345d9586a709c08a1095f2635833d075c45fe7be 100644 (file)
@@ -54,6 +54,24 @@ static int option_parse_x(const struct option *opt,
        return 0;
 }
 
+static int option_parse_m(const struct option *opt,
+                         const char *arg, int unset)
+{
+       struct replay_opts *replay = opt->value;
+       char *end;
+
+       if (unset) {
+               replay->mainline = 0;
+               return 0;
+       }
+
+       replay->mainline = strtol(arg, &end, 10);
+       if (*end || replay->mainline <= 0)
+               return opterror(opt, "expects a number greater than zero", 0);
+
+       return 0;
+}
+
 LAST_ARG_MUST_BE_NULL
 static void verify_opt_compatible(const char *me, const char *base_opt, ...)
 {
@@ -84,7 +102,8 @@ static int run_sequencer(int argc, const char **argv, struct replay_opts *opts)
                OPT_BOOL('e', "edit", &opts->edit, N_("edit the commit message")),
                OPT_NOOP_NOARG('r', NULL),
                OPT_BOOL('s', "signoff", &opts->signoff, N_("add Signed-off-by:")),
-               OPT_INTEGER('m', "mainline", &opts->mainline, N_("parent number")),
+               OPT_CALLBACK('m', "mainline", opts, N_("parent-number"),
+                            N_("select mainline parent"), option_parse_m),
                OPT_RERERE_AUTOUPDATE(&opts->allow_rerere_auto),
                OPT_STRING(0, "strategy", &opts->strategy, N_("strategy"), N_("merge strategy")),
                OPT_CALLBACK('X', "strategy-option", &opts, N_("option"),
index 1ff5a6753803f8c2ccb5e66dcb926582bd25fd37..b8e2e74fe0c5cdefa6ed4d6c309a8bba203cc6e8 100644 (file)
@@ -131,8 +131,8 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        const char *dest = NULL;
        int fd[2];
        struct child_process *conn;
-       struct sha1_array extra_have = SHA1_ARRAY_INIT;
-       struct sha1_array shallow = SHA1_ARRAY_INIT;
+       struct oid_array extra_have = OID_ARRAY_INIT;
+       struct oid_array shallow = OID_ARRAY_INIT;
        struct ref *remote_refs, *local_refs;
        int ret;
        int helper_status = 0;
@@ -144,6 +144,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        unsigned force_update = 0;
        unsigned quiet = 0;
        int push_cert = 0;
+       struct string_list push_options = STRING_LIST_INIT_NODUP;
        unsigned use_thin_pack = 0;
        unsigned atomic = 0;
        unsigned stateless_rpc = 0;
@@ -165,6 +166,9 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
                { OPTION_CALLBACK,
                  0, "signed", &push_cert, "yes|no|if-asked", N_("GPG sign the push"),
                  PARSE_OPT_OPTARG, option_parse_push_signed },
+               OPT_STRING_LIST(0, "push-option", &push_options,
+                               N_("server-specific"),
+                               N_("option to transmit")),
                OPT_BOOL(0, "progress", &progress, N_("force progress reporting")),
                OPT_BOOL(0, "thin", &use_thin_pack, N_("use thin pack")),
                OPT_BOOL(0, "atomic", &atomic, N_("request atomic transaction on remote side")),
@@ -199,6 +203,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
        args.use_thin_pack = use_thin_pack;
        args.atomic = atomic;
        args.stateless_rpc = stateless_rpc;
+       args.push_options = push_options.nr ? &push_options : NULL;
 
        if (from_stdin) {
                struct argv_array all_refspecs = ARGV_ARRAY_INIT;
index f78bb4818d7a5254d7c3c2c34d1f370cd3aa9327..7cff1839fc15f46d58c9f78361f362d1d790cbd9 100644 (file)
@@ -149,7 +149,6 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
        ctx.fmt = CMIT_FMT_USERFORMAT;
        ctx.abbrev = log->abbrev;
        ctx.print_email_subject = 1;
-       ctx.after_subject = "";
        ctx.date_mode.type = DATE_NORMAL;
        ctx.output_encoding = get_log_output_encoding();
 
index 15a5430c0028f45ff6265a821ff85aa9d8445230..36e4231821c0acbb6fe21948c752d4ee7c96405d 100644 (file)
@@ -270,6 +270,29 @@ static int module_list_compute(int argc, const char **argv,
        return result;
 }
 
+static void module_list_active(struct module_list *list)
+{
+       int i;
+       struct module_list active_modules = MODULE_LIST_INIT;
+
+       gitmodules_config();
+
+       for (i = 0; i < list->nr; i++) {
+               const struct cache_entry *ce = list->entries[i];
+
+               if (!is_submodule_initialized(ce->name))
+                       continue;
+
+               ALLOC_GROW(active_modules.entries,
+                          active_modules.nr + 1,
+                          active_modules.alloc);
+               active_modules.entries[active_modules.nr++] = ce;
+       }
+
+       free(list->entries);
+       *list = active_modules;
+}
+
 static int module_list(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -333,6 +356,18 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
                die(_("No url found for submodule path '%s' in .gitmodules"),
                        displaypath);
 
+       /*
+        * NEEDSWORK: In a multi-working-tree world, this needs to be
+        * set in the per-worktree config.
+        *
+        * Set active flag for the submodule being initialized
+        */
+       if (!is_submodule_initialized(path)) {
+               strbuf_reset(&sb);
+               strbuf_addf(&sb, "submodule.%s.active", sub->name);
+               git_config_set_gently(sb.buf, "true");
+       }
+
        /*
         * Copy url setting when it is not set yet.
         * To look up the url in .git/config, we must not fall back to
@@ -420,6 +455,13 @@ static int module_init(int argc, const char **argv, const char *prefix)
        if (module_list_compute(argc, argv, prefix, &pathspec, &list) < 0)
                return 1;
 
+       /*
+        * If there are no path args and submodule.active is set then,
+        * by default, only initialize 'active' modules.
+        */
+       if (!argc && git_config_get_value_multi("submodule.active"))
+               module_list_active(&list);
+
        for (i = 0; i < list.nr; i++)
                init_submodule(list.entries[i]->name, prefix, quiet);
 
@@ -577,9 +619,7 @@ static int module_clone(int argc, const char **argv, const char *prefix)
        const char *name = NULL, *url = NULL, *depth = NULL;
        int quiet = 0;
        int progress = 0;
-       FILE *submodule_dot_git;
        char *p, *path = NULL, *sm_gitdir;
-       struct strbuf rel_path = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
        struct string_list reference = STRING_LIST_INIT_NODUP;
        char *sm_alternate = NULL, *error_strategy = NULL;
@@ -651,27 +691,12 @@ static int module_clone(int argc, const char **argv, const char *prefix)
                strbuf_reset(&sb);
        }
 
-       /* Write a .git file in the submodule to redirect to the superproject. */
-       strbuf_addf(&sb, "%s/.git", path);
-       if (safe_create_leading_directories_const(sb.buf) < 0)
-               die(_("could not create leading directories of '%s'"), sb.buf);
-       submodule_dot_git = fopen(sb.buf, "w");
-       if (!submodule_dot_git)
-               die_errno(_("cannot open file '%s'"), sb.buf);
-
-       fprintf_or_die(submodule_dot_git, "gitdir: %s\n",
-                      relative_path(sm_gitdir, path, &rel_path));
-       if (fclose(submodule_dot_git))
-               die(_("could not close file %s"), sb.buf);
-       strbuf_reset(&sb);
-       strbuf_reset(&rel_path);
+       /* Connect module worktree and git dir */
+       connect_work_tree_and_git_dir(path, sm_gitdir);
 
-       /* Redirect the worktree of the submodule in the superproject's config */
        p = git_pathdup_submodule(path, "config");
        if (!p)
                die(_("could not get submodule directory for '%s'"), path);
-       git_config_set_in_file(p, "core.worktree",
-                              relative_path(path, sm_gitdir, &rel_path));
 
        /* setup alternateLocation and alternateErrorStrategy in the cloned submodule if needed */
        git_config_get_string("submodule.alternateLocation", &sm_alternate);
@@ -687,7 +712,6 @@ static int module_clone(int argc, const char **argv, const char *prefix)
        free(error_strategy);
 
        strbuf_release(&sb);
-       strbuf_release(&rel_path);
        free(sm_gitdir);
        free(path);
        free(p);
@@ -759,7 +783,6 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
        struct strbuf displaypath_sb = STRBUF_INIT;
        struct strbuf sb = STRBUF_INIT;
        const char *displaypath = NULL;
-       char *url = NULL;
        int needs_cloning = 0;
 
        if (ce_stage(ce)) {
@@ -793,15 +816,8 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                goto cleanup;
        }
 
-       /*
-        * Looking up the url in .git/config.
-        * We must not fall back to .gitmodules as we only want
-        * to process configured submodules.
-        */
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "submodule.%s.url", sub->name);
-       git_config_get_string(sb.buf, &url);
-       if (!url) {
+       /* Check if the submodule has been initialized. */
+       if (!is_submodule_initialized(ce->name)) {
                next_submodule_warn_missing(suc, out, displaypath);
                goto cleanup;
        }
@@ -835,7 +851,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                argv_array_push(&child->args, "--depth=1");
        argv_array_pushl(&child->args, "--path", sub->path, NULL);
        argv_array_pushl(&child->args, "--name", sub->name, NULL);
-       argv_array_pushl(&child->args, "--url", url, NULL);
+       argv_array_pushl(&child->args, "--url", sub->url, NULL);
        if (suc->references.nr) {
                struct string_list_item *item;
                for_each_string_list_item(item, &suc->references)
@@ -845,7 +861,6 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,
                argv_array_push(&child->args, suc->depth);
 
 cleanup:
-       free(url);
        strbuf_reset(&displaypath_sb);
        strbuf_reset(&sb);
 
@@ -1090,6 +1105,50 @@ static int resolve_remote_submodule_branch(int argc, const char **argv,
        return 0;
 }
 
+static int push_check(int argc, const char **argv, const char *prefix)
+{
+       struct remote *remote;
+
+       if (argc < 2)
+               die("submodule--helper push-check requires at least 1 argument");
+
+       /*
+        * The remote must be configured.
+        * This is to avoid pushing to the exact same URL as the parent.
+        */
+       remote = pushremote_get(argv[1]);
+       if (!remote || remote->origin == REMOTE_UNCONFIGURED)
+               die("remote '%s' not configured", argv[1]);
+
+       /* Check the refspec */
+       if (argc > 2) {
+               int i, refspec_nr = argc - 2;
+               struct ref *local_refs = get_local_heads();
+               struct refspec *refspec = parse_push_refspec(refspec_nr,
+                                                            argv + 2);
+
+               for (i = 0; i < refspec_nr; i++) {
+                       struct refspec *rs = refspec + i;
+
+                       if (rs->pattern || rs->matching)
+                               continue;
+
+                       /*
+                        * LHS must match a single ref
+                        * NEEDSWORK: add logic to special case 'HEAD' once
+                        * working with submodules in a detached head state
+                        * ceases to be the norm.
+                        */
+                       if (count_refspec_match(rs->src, local_refs, NULL) != 1)
+                               die("src refspec '%s' must name a ref",
+                                   rs->src);
+               }
+               free_refspec(refspec_nr, refspec);
+       }
+
+       return 0;
+}
+
 static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
 {
        int i;
@@ -1127,6 +1186,16 @@ static int absorb_git_dirs(int argc, const char **argv, const char *prefix)
        return 0;
 }
 
+static int is_active(int argc, const char **argv, const char *prefix)
+{
+       if (argc != 2)
+               die("submodule--helper is-active takes exactly 1 argument");
+
+       gitmodules_config();
+
+       return !is_submodule_initialized(argv[1]);
+}
+
 #define SUPPORT_SUPER_PREFIX (1<<0)
 
 struct cmd_struct {
@@ -1145,7 +1214,9 @@ static struct cmd_struct commands[] = {
        {"resolve-relative-url-test", resolve_relative_url_test, 0},
        {"init", module_init, SUPPORT_SUPER_PREFIX},
        {"remote-branch", resolve_remote_submodule_branch, 0},
+       {"push-check", push_check, 0},
        {"absorb-git-dirs", absorb_git_dirs, SUPPORT_SUPER_PREFIX},
+       {"is-active", is_active, 0},
 };
 
 int cmd_submodule__helper(int argc, const char **argv, const char *prefix)
index ad29be692384454ffd2f35f134d49c227ae1a67b..222404522fd8db970c5f832338095c6947548ac9 100644 (file)
@@ -22,7 +22,7 @@
 static const char * const git_tag_usage[] = {
        N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
        N_("git tag -d <tagname>..."),
-       N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
+       N_("git tag -l [-n[<num>]] [--contains <commit>] [--no-contains <commit>] [--points-at <object>]"
                "\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
        N_("git tag -v [--format=<format>] <tagname>..."),
        NULL
@@ -72,25 +72,22 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
                             const void *cb_data)
 {
        const char **p;
-       char ref[PATH_MAX];
+       struct strbuf ref = STRBUF_INIT;
        int had_error = 0;
        unsigned char sha1[20];
 
        for (p = argv; *p; p++) {
-               if (snprintf(ref, sizeof(ref), "refs/tags/%s", *p)
-                                       >= sizeof(ref)) {
-                       error(_("tag name too long: %.*s..."), 50, *p);
-                       had_error = 1;
-                       continue;
-               }
-               if (read_ref(ref, sha1)) {
+               strbuf_reset(&ref);
+               strbuf_addf(&ref, "refs/tags/%s", *p);
+               if (read_ref(ref.buf, sha1)) {
                        error(_("tag '%s' not found."), *p);
                        had_error = 1;
                        continue;
                }
-               if (fn(*p, ref, sha1, cb_data))
+               if (fn(*p, ref.buf, sha1, cb_data))
                        had_error = 1;
        }
+       strbuf_release(&ref);
        return had_error;
 }
 
@@ -231,26 +228,22 @@ static void create_tag(const unsigned char *object, const char *tag,
                       unsigned char *prev, unsigned char *result)
 {
        enum object_type type;
-       char header_buf[1024];
-       int header_len;
+       struct strbuf header = STRBUF_INIT;
        char *path = NULL;
 
        type = sha1_object_info(object, NULL);
        if (type <= OBJ_NONE)
            die(_("bad object type."));
 
-       header_len = snprintf(header_buf, sizeof(header_buf),
-                         "object %s\n"
-                         "type %s\n"
-                         "tag %s\n"
-                         "tagger %s\n\n",
-                         sha1_to_hex(object),
-                         typename(type),
-                         tag,
-                         git_committer_info(IDENT_STRICT));
-
-       if (header_len > sizeof(header_buf) - 1)
-               die(_("tag header too big."));
+       strbuf_addf(&header,
+                   "object %s\n"
+                   "type %s\n"
+                   "tag %s\n"
+                   "tagger %s\n\n",
+                   sha1_to_hex(object),
+                   typename(type),
+                   tag,
+                   git_committer_info(IDENT_STRICT));
 
        if (!opt->message_given) {
                int fd;
@@ -288,7 +281,8 @@ static void create_tag(const unsigned char *object, const char *tag,
        if (!opt->message_given && !buf->len)
                die(_("no tag message?"));
 
-       strbuf_insert(buf, 0, header_buf, header_len);
+       strbuf_insert(buf, 0, header.buf, header.len);
+       strbuf_release(&header);
 
        if (build_tag_object(buf, opt->sign, result) < 0) {
                if (path)
@@ -424,14 +418,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                OPT_GROUP(N_("Tag listing options")),
                OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
                OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
+               OPT_NO_CONTAINS(&filter.no_commit, N_("print only tags that don't contain the commit")),
                OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
+               OPT_WITHOUT(&filter.no_commit, N_("print only tags that don't contain the commit")),
                OPT_MERGED(&filter, N_("print only tags that are merged")),
                OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
                OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
                             N_("field name to sort on"), &parse_opt_ref_sorting),
                {
                        OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
-                       N_("print only tags of the object"), 0, parse_opt_object_name
+                       N_("print only tags of the object"), PARSE_OPT_LASTARG_DEFAULT,
+                       parse_opt_object_name, (intptr_t) "HEAD"
                },
                OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
@@ -454,8 +451,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        }
        create_tag_object = (opt.sign || annotate || msg.given || msgfile);
 
-       if (argc == 0 && !cmdmode)
-               cmdmode = 'l';
+       if (!cmdmode) {
+               if (argc == 0)
+                       cmdmode = 'l';
+               else if (filter.with_commit || filter.no_commit ||
+                        filter.points_at.nr || filter.merge_commit ||
+                        filter.lines != -1)
+                       cmdmode = 'l';
+       }
 
        if ((create_tag_object || force) && (cmdmode != 0))
                usage_with_options(git_tag_usage, options);
@@ -485,13 +488,15 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                return ret;
        }
        if (filter.lines != -1)
-               die(_("-n option is only allowed with -l."));
+               die(_("-n option is only allowed in list mode"));
        if (filter.with_commit)
-               die(_("--contains option is only allowed with -l."));
+               die(_("--contains option is only allowed in list mode"));
+       if (filter.no_commit)
+               die(_("--no-contains option is only allowed in list mode"));
        if (filter.points_at.nr)
-               die(_("--points-at option is only allowed with -l."));
+               die(_("--points-at option is only allowed in list mode"));
        if (filter.merge_commit)
-               die(_("--merged and --no-merged option are only allowed with -l"));
+               die(_("--merged and --no-merged options are only allowed in list mode"));
        if (cmdmode == 'd')
                return for_each_tag_name(argv, delete_tag, NULL);
        if (cmdmode == 'v') {
index d74d72cc7fad93980abd409d2c9dc10ae4652fd3..ebfc09faa0d604218af8f5815af5e5fee5915158 100644 (file)
@@ -125,12 +125,16 @@ static int test_if_untracked_cache_is_supported(void)
        struct stat st;
        struct stat_data base;
        int fd, ret = 0;
+       char *cwd;
 
        strbuf_addstr(&mtime_dir, "mtime-test-XXXXXX");
        if (!mkdtemp(mtime_dir.buf))
                die_errno("Could not make temporary directory");
 
-       fprintf(stderr, _("Testing mtime in '%s' "), xgetcwd());
+       cwd = xgetcwd();
+       fprintf(stderr, _("Testing mtime in '%s' "), cwd);
+       free(cwd);
+
        atexit(remove_test_directory);
        xstat_mtime_dir(&st);
        fill_stat_data(&base, &st);
index 831fe058a53da95643b1a93038f576d5bffea7ef..9993ded41aaaaa9a9291bf51b9949a97418a5e76 100644 (file)
@@ -318,7 +318,8 @@ static int add(int ac, const char **av, const char *prefix)
 {
        struct add_opts opts;
        const char *new_branch_force = NULL;
-       const char *path, *branch;
+       char *path;
+       const char *branch;
        struct option options[] = {
                OPT__FORCE(&opts.force, N_("checkout <branch> even if already checked out in other worktree")),
                OPT_STRING('b', NULL, &opts.new_branch, N_("branch"),
@@ -338,7 +339,7 @@ static int add(int ac, const char **av, const char *prefix)
        if (ac < 1 || ac > 2)
                usage_with_options(worktree_usage, options);
 
-       path = prefix_filename(prefix, strlen(prefix), av[0]);
+       path = prefix_filename(prefix, av[0]);
        branch = ac < 2 ? "HEAD" : av[1];
 
        if (!strcmp(branch, "-"))
index 991b4a13e2491093ed9e9942ac116843973f65ff..ddb6070c4c24653a4fa8699251d11626880c2e5c 100644 (file)
@@ -105,7 +105,7 @@ static int stream_to_pack(struct bulk_checkin_state *state,
 
        git_deflate_init(&s, pack_compression_level);
 
-       hdrlen = encode_in_pack_object_header(type, size, obuf);
+       hdrlen = encode_in_pack_object_header(obuf, sizeof(obuf), type, size);
        s.next_out = obuf + hdrlen;
        s.avail_out = sizeof(obuf) - hdrlen;
 
diff --git a/cache.h b/cache.h
index dc261f40dda7c6eb274463a64767671a6c1cae4a..ef0fe43a9df9437bac8e1f78cc1a908798b290fc 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -10,8 +10,8 @@
 #include "trace.h"
 #include "string-list.h"
 #include "pack-revindex.h"
+#include "hash.h"
 
-#include SHA1_HEADER
 #ifndef platform_SHA_CTX
 /*
  * platform's underlying implementation of SHA-1; could be OpenSSL,
@@ -66,8 +66,12 @@ unsigned long git_deflate_bound(git_zstream *, unsigned long);
 #define GIT_SHA1_RAWSZ 20
 #define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)
 
+/* The length in byte and in hex digits of the largest possible hash value. */
+#define GIT_MAX_RAWSZ GIT_SHA1_RAWSZ
+#define GIT_MAX_HEXSZ GIT_SHA1_HEXSZ
+
 struct object_id {
-       unsigned char hash[GIT_SHA1_RAWSZ];
+       unsigned char hash[GIT_MAX_RAWSZ];
 };
 
 #if defined(DT_UNKNOWN) && !defined(NO_D_TYPE_IN_DIRENT)
@@ -343,6 +347,7 @@ struct index_state {
 extern struct index_state the_index;
 
 /* Name hashing */
+extern int test_lazy_init_name_hash(struct index_state *istate, int try_threaded);
 extern void add_name_hash(struct index_state *istate, struct cache_entry *ce);
 extern void remove_name_hash(struct index_state *istate, struct cache_entry *ce);
 extern void free_name_hash(struct index_state *istate);
@@ -410,6 +415,7 @@ static inline enum object_type object_type(unsigned int mode)
 #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
 #define GIT_SUPER_PREFIX_ENVIRONMENT "GIT_INTERNAL_SUPER_PREFIX"
+#define GIT_TOPLEVEL_PREFIX_ENVIRONMENT "GIT_INTERNAL_TOPLEVEL_PREFIX"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -518,11 +524,30 @@ extern void set_git_work_tree(const char *tree);
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
 extern void setup_work_tree(void);
+/*
+ * Find GIT_DIR of the repository that contains the current working directory,
+ * without changing the working directory or other global state. The result is
+ * appended to gitdir. The return value is either NULL if no repository was
+ * found, or pointing to the path inside gitdir's buffer.
+ */
+extern const char *discover_git_directory(struct strbuf *gitdir);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);
 extern char *prefix_path(const char *prefix, int len, const char *path);
 extern char *prefix_path_gently(const char *prefix, int len, int *remaining, const char *path);
-extern const char *prefix_filename(const char *prefix, int len, const char *path);
+
+/*
+ * Concatenate "prefix" (if len is non-zero) and "path", with no
+ * connecting characters (so "prefix" should end with a "/").
+ * Unlike prefix_path, this should be used if the named file does
+ * not have to interact with index entry; i.e. name of a random file
+ * on the filesystem.
+ *
+ * The return value is always a newly allocated string (even if the
+ * prefix was empty).
+ */
+extern char *prefix_filename(const char *prefix, const char *path);
+
 extern int check_filename(const char *prefix, const char *name);
 extern void verify_filename(const char *prefix,
                            const char *name,
@@ -685,6 +710,8 @@ extern void update_index_if_able(struct index_state *, struct lock_file *);
 extern int hold_locked_index(struct lock_file *, int);
 extern void set_alternate_index_output(const char *);
 
+extern int verify_index_checksum;
+
 /* Environment bits from configuration mechanism */
 extern int trust_executable_bit;
 extern int trust_ctime;
@@ -957,7 +984,7 @@ extern char *sha1_pack_index_name(const unsigned char *sha1);
 extern const char *find_unique_abbrev(const unsigned char *sha1, int len);
 extern int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len);
 
-extern const unsigned char null_sha1[GIT_SHA1_RAWSZ];
+extern const unsigned char null_sha1[GIT_MAX_RAWSZ];
 extern const struct object_id null_oid;
 
 static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)
@@ -1139,7 +1166,7 @@ typedef int create_file_fn(const char *path, void *cb);
 int raceproof_create_file(const char *path, create_file_fn fn, void *cb);
 
 int mkdir_in_gitdir(const char *path);
-extern char *expand_user_path(const char *path);
+extern char *expand_user_path(const char *path, int real_home);
 const char *enter_repo(const char *path, int strict);
 static inline int is_absolute_path(const char *path)
 {
@@ -1169,6 +1196,13 @@ extern int is_ntfs_dotgit(const char *name);
  */
 extern char *xdg_config_home(const char *filename);
 
+/**
+ * Return a newly allocated string with the evaluation of
+ * "$XDG_CACHE_HOME/git/$filename" if $XDG_CACHE_HOME is non-empty, otherwise
+ * "$HOME/.cache/git/$filename". Return NULL upon error.
+ */
+extern char *xdg_cache_home(const char *filename);
+
 /* object replacement */
 #define LOOKUP_REPLACE_OBJECT 1
 #define LOOKUP_UNKNOWN_OBJECT 2
@@ -1332,7 +1366,7 @@ extern int get_sha1_with_context(const char *str, unsigned flags, unsigned char
 
 extern int get_oid(const char *str, struct object_id *oid);
 
-typedef int each_abbrev_fn(const unsigned char *sha1, void *);
+typedef int each_abbrev_fn(const struct object_id *oid, void *);
 extern int for_each_abbrev(const char *prefix, each_abbrev_fn, void *);
 
 extern int set_disambiguate_hint_config(const char *var, const char *value);
@@ -1646,6 +1680,30 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 
 extern void pack_report(void);
 
+/*
+ * Create a temporary file rooted in the object database directory, or
+ * die on failure. The filename is taken from "pattern", which should have the
+ * usual "XXXXXX" trailer, and the resulting filename is written into the
+ * "template" buffer. Returns the open descriptor.
+ */
+extern int odb_mkstemp(struct strbuf *template, const char *pattern);
+
+/*
+ * Generate the filename to be used for a pack file with checksum "sha1" and
+ * extension "ext". The result is written into the strbuf "buf", overwriting
+ * any existing contents. A pointer to buf->buf is returned as a convenience.
+ *
+ * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
+ */
+extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
+
+/*
+ * Create a pack .keep file named "name" (which should generally be the output
+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
+ * error.
+ */
+extern int odb_pack_keep(const char *name);
+
 /*
  * mmap the index file for the specified packfile (if it is not
  * already mmapped).  Return 0 on success.
@@ -1842,6 +1900,7 @@ extern int git_config_from_blob_sha1(config_fn_t fn, const char *name,
                                     const unsigned char *sha1, void *data);
 extern void git_config_push_parameter(const char *text);
 extern int git_config_from_parameters(config_fn_t fn, void *data);
+extern void read_early_config(config_fn_t cb, void *data);
 extern void git_config(config_fn_t fn, void *);
 extern int git_config_with_options(config_fn_t fn, void *,
                                   struct git_config_source *config_source,
@@ -1851,6 +1910,7 @@ extern int git_parse_maybe_bool(const char *);
 extern int git_config_int(const char *, const char *);
 extern int64_t git_config_int64(const char *, const char *);
 extern unsigned long git_config_ulong(const char *, const char *);
+extern ssize_t git_config_ssize_t(const char *, const char *);
 extern int git_config_bool_or_int(const char *, const char *, int *);
 extern int git_config_bool(const char *, const char *);
 extern int git_config_maybe_bool(const char *, const char *);
diff --git a/ci/run-windows-build.sh b/ci/run-windows-build.sh
new file mode 100755 (executable)
index 0000000..4e3a50b
--- /dev/null
@@ -0,0 +1,74 @@
+#!/usr/bin/env bash
+#
+# Script to trigger the a Git for Windows build and test run.
+# Set the $GFW_CI_TOKEN as environment variable.
+# Pass the branch (only branches on https://github.com/git/git are
+# supported) and a commit hash.
+#
+
+test $# -ne 2 && echo "Unexpected number of parameters" && exit 1
+test -z "$GFW_CI_TOKEN" && echo "GFW_CI_TOKEN not defined" && exit
+
+BRANCH=$1
+COMMIT=$2
+
+gfwci () {
+       local CURL_ERROR_CODE HTTP_CODE
+       exec 3>&1
+       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) \
+               "https://git-for-windows-ci.azurewebsites.net/api/TestNow?$1" \
+       )
+       CURL_ERROR_CODE=$?
+       if test $CURL_ERROR_CODE -ne 0
+       then
+               return $CURL_ERROR_CODE
+       fi
+       if test "$HTTP_CODE" -ge 400 && test "$HTTP_CODE" -lt 600
+       then
+               return 127
+       fi
+}
+
+# Trigger build job
+BUILD_ID=$(gfwci "action=trigger&branch=$BRANCH&commit=$COMMIT&skipTests=false")
+if test $? -ne 0
+then
+       echo "Unable to trigger Visual Studio Team Services Build"
+       echo "$BUILD_ID"
+       exit 1
+fi
+
+# Check if the $BUILD_ID contains a number
+case $BUILD_ID in
+''|*[!0-9]*) echo "Unexpected build number: $BUILD_ID" && exit 1
+esac
+
+echo "Visual Studio Team Services Build #${BUILD_ID}"
+
+# Wait until build job finished
+STATUS=
+RESULT=
+while true
+do
+       LAST_STATUS=$STATUS
+       STATUS=$(gfwci "action=status&buildId=$BUILD_ID")
+       test "$STATUS" = "$LAST_STATUS" || printf "\nStatus: $STATUS "
+       printf "."
+
+       case "$STATUS" in
+       inProgress|postponed|notStarted) sleep 10               ;; # continue
+                "completed: succeeded") RESULT="success"; break;; # success
+       *) echo "Unhandled status: $STATUS";               break;; # failure
+       esac
+done
+
+# Print log
+echo ""
+echo ""
+gfwci "action=log&buildId=$BUILD_ID" | cut -c 30-
+
+# Set exit code for TravisCI
+test "$RESULT" = "success"
index 59501db99a74ab8f237766730045dda4ecd112b2..2848034fe9c3f3cc1d930347e892cee1cbed4f52 100644 (file)
@@ -292,9 +292,10 @@ static char *grab_blob(const struct object_id *oid, unsigned int mode,
        enum object_type type;
 
        if (S_ISGITLINK(mode)) {
-               blob = xmalloc(100);
-               *size = snprintf(blob, 100,
-                                "Subproject commit %s\n", oid_to_hex(oid));
+               struct strbuf buf = STRBUF_INIT;
+               strbuf_addf(&buf, "Subproject commit %s\n", oid_to_hex(oid));
+               *size = buf.len;
+               blob = strbuf_detach(&buf, NULL);
        } else if (is_null_oid(oid)) {
                /* deleted blob */
                *size = 0;
@@ -1311,7 +1312,7 @@ static const char *path_path(void *obj)
 
 /* find set of paths that every parent touches */
 static struct combine_diff_path *find_paths_generic(const unsigned char *sha1,
-       const struct sha1_array *parents, struct diff_options *opt)
+       const struct oid_array *parents, struct diff_options *opt)
 {
        struct combine_diff_path *paths = NULL;
        int i, num_parent = parents->nr;
@@ -1335,7 +1336,7 @@ static struct combine_diff_path *find_paths_generic(const unsigned char *sha1,
                        opt->output_format = stat_opt;
                else
                        opt->output_format = DIFF_FORMAT_NO_OUTPUT;
-               diff_tree_sha1(parents->sha1[i], sha1, "", opt);
+               diff_tree_sha1(parents->oid[i].hash, sha1, "", opt);
                diffcore_std(opt);
                paths = intersect_paths(paths, i, num_parent);
 
@@ -1359,7 +1360,7 @@ static struct combine_diff_path *find_paths_generic(const unsigned char *sha1,
  * rename/copy detection, etc, comparing all trees simultaneously (= faster).
  */
 static struct combine_diff_path *find_paths_multitree(
-       const unsigned char *sha1, const struct sha1_array *parents,
+       const unsigned char *sha1, const struct oid_array *parents,
        struct diff_options *opt)
 {
        int i, nparent = parents->nr;
@@ -1369,7 +1370,7 @@ static struct combine_diff_path *find_paths_multitree(
 
        ALLOC_ARRAY(parents_sha1, nparent);
        for (i = 0; i < nparent; i++)
-               parents_sha1[i] = parents->sha1[i];
+               parents_sha1[i] = parents->oid[i].hash;
 
        /* fake list head, so worker can assume it is non-NULL */
        paths_head.next = NULL;
@@ -1384,7 +1385,7 @@ static struct combine_diff_path *find_paths_multitree(
 
 
 void diff_tree_combined(const unsigned char *sha1,
-                       const struct sha1_array *parents,
+                       const struct oid_array *parents,
                        int dense,
                        struct rev_info *rev)
 {
@@ -1462,7 +1463,7 @@ void diff_tree_combined(const unsigned char *sha1,
                if (stat_opt) {
                        diffopts.output_format = stat_opt;
 
-                       diff_tree_sha1(parents->sha1[0], sha1, "", &diffopts);
+                       diff_tree_sha1(parents->oid[0].hash, sha1, "", &diffopts);
                        diffcore_std(&diffopts);
                        if (opt->orderfile)
                                diffcore_order(opt->orderfile);
@@ -1532,12 +1533,12 @@ void diff_tree_combined_merge(const struct commit *commit, int dense,
                              struct rev_info *rev)
 {
        struct commit_list *parent = get_saved_parents(rev, commit);
-       struct sha1_array parents = SHA1_ARRAY_INIT;
+       struct oid_array parents = OID_ARRAY_INIT;
 
        while (parent) {
-               sha1_array_append(&parents, parent->item->object.oid.hash);
+               oid_array_append(&parents, &parent->item->object.oid);
                parent = parent->next;
        }
        diff_tree_combined(commit->object.oid.hash, &parents, dense, rev);
-       sha1_array_clear(&parents);
+       oid_array_clear(&parents);
 }
index 528272ac9bacebf263c3c9a33bad4dd81bddd4d8..7b1986d5c8a0120ea90ee485ba46aff912de60fa 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -261,7 +261,7 @@ extern struct commit_list *get_merge_bases_many_dirty(struct commit *one, int n,
 /* largest positive number a signed 32-bit integer can contain */
 #define INFINITE_DEPTH 0x7fffffff
 
-struct sha1_array;
+struct oid_array;
 struct ref;
 extern int register_shallow(const unsigned char *sha1);
 extern int unregister_shallow(const unsigned char *sha1);
@@ -273,18 +273,18 @@ extern struct commit_list *get_shallow_commits_by_rev_list(
                int ac, const char **av, int shallow_flag, int not_shallow_flag);
 extern void set_alternate_shallow_file(const char *path, int override);
 extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
-                                const struct sha1_array *extra);
+                                const struct oid_array *extra);
 extern void setup_alternate_shallow(struct lock_file *shallow_lock,
                                    const char **alternate_shallow_file,
-                                   const struct sha1_array *extra);
-extern const char *setup_temporary_shallow(const struct sha1_array *extra);
+                                   const struct oid_array *extra);
+extern const char *setup_temporary_shallow(const struct oid_array *extra);
 extern void advertise_shallow_grafts(int);
 
 struct shallow_info {
-       struct sha1_array *shallow;
+       struct oid_array *shallow;
        int *ours, nr_ours;
        int *theirs, nr_theirs;
-       struct sha1_array *ref;
+       struct oid_array *ref;
 
        /* for receive-pack */
        uint32_t **used_shallow;
@@ -295,7 +295,7 @@ struct shallow_info {
        int nr_commits;
 };
 
-extern void prepare_shallow_info(struct shallow_info *, struct sha1_array *);
+extern void prepare_shallow_info(struct shallow_info *, struct oid_array *);
 extern void clear_shallow_info(struct shallow_info *);
 extern void remove_nonexistent_theirs_shallow(struct shallow_info *);
 extern void assign_shallow_commits_to_refs(struct shallow_info *info,
index bd286b3e3610b23e289bd1aa55e4cc2e79ec6590..0daaed338eabecb209e42ee5944e1dc432f94070 100644 (file)
--- a/config.c
+++ b/config.c
@@ -13,6 +13,7 @@
 #include "hashmap.h"
 #include "string-list.h"
 #include "utf8.h"
+#include "dir.h"
 
 struct config_source {
        struct config_source *prev;
@@ -134,7 +135,7 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        if (!path)
                return config_error_nonbool("include.path");
 
-       expanded = expand_user_path(path);
+       expanded = expand_user_path(path, 0);
        if (!expanded)
                return error("could not expand include path '%s'", path);
        path = expanded;
@@ -170,9 +171,94 @@ static int handle_path_include(const char *path, struct config_include_data *inc
        return ret;
 }
 
+static int prepare_include_condition_pattern(struct strbuf *pat)
+{
+       struct strbuf path = STRBUF_INIT;
+       char *expanded;
+       int prefix = 0;
+
+       expanded = expand_user_path(pat->buf, 1);
+       if (expanded) {
+               strbuf_reset(pat);
+               strbuf_addstr(pat, expanded);
+               free(expanded);
+       }
+
+       if (pat->buf[0] == '.' && is_dir_sep(pat->buf[1])) {
+               const char *slash;
+
+               if (!cf || !cf->path)
+                       return error(_("relative config include "
+                                      "conditionals must come from files"));
+
+               strbuf_realpath(&path, cf->path, 1);
+               slash = find_last_dir_sep(path.buf);
+               if (!slash)
+                       die("BUG: how is this possible?");
+               strbuf_splice(pat, 0, 1, path.buf, slash - path.buf);
+               prefix = slash - path.buf + 1 /* slash */;
+       } else if (!is_absolute_path(pat->buf))
+               strbuf_insert(pat, 0, "**/", 3);
+
+       if (pat->len && is_dir_sep(pat->buf[pat->len - 1]))
+               strbuf_addstr(pat, "**");
+
+       strbuf_release(&path);
+       return prefix;
+}
+
+static int include_by_gitdir(const char *cond, size_t cond_len, int icase)
+{
+       struct strbuf text = STRBUF_INIT;
+       struct strbuf pattern = STRBUF_INIT;
+       int ret = 0, prefix;
+
+       strbuf_realpath(&text, get_git_dir(), 1);
+       strbuf_add(&pattern, cond, cond_len);
+       prefix = prepare_include_condition_pattern(&pattern);
+
+       if (prefix < 0)
+               goto done;
+
+       if (prefix > 0) {
+               /*
+                * perform literal matching on the prefix part so that
+                * any wildcard character in it can't create side effects.
+                */
+               if (text.len < prefix)
+                       goto done;
+               if (!icase && strncmp(pattern.buf, text.buf, prefix))
+                       goto done;
+               if (icase && strncasecmp(pattern.buf, text.buf, prefix))
+                       goto done;
+       }
+
+       ret = !wildmatch(pattern.buf + prefix, text.buf + prefix,
+                        icase ? WM_CASEFOLD : 0, NULL);
+
+done:
+       strbuf_release(&pattern);
+       strbuf_release(&text);
+       return ret;
+}
+
+static int include_condition_is_true(const char *cond, size_t cond_len)
+{
+
+       if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
+               return include_by_gitdir(cond, cond_len, 0);
+       else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
+               return include_by_gitdir(cond, cond_len, 1);
+
+       /* unknown conditionals are always false */
+       return 0;
+}
+
 int git_config_include(const char *var, const char *value, void *data)
 {
        struct config_include_data *inc = data;
+       const char *cond, *key;
+       int cond_len;
        int ret;
 
        /*
@@ -185,6 +271,12 @@ int git_config_include(const char *var, const char *value, void *data)
 
        if (!strcmp(var, "include.path"))
                ret = handle_path_include(value, inc);
+
+       if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
+           (cond && include_condition_is_true(cond, cond_len)) &&
+           !strcmp(key, "path"))
+               ret = handle_path_include(value, inc);
+
        return ret;
 }
 
@@ -742,6 +834,15 @@ int git_parse_ulong(const char *value, unsigned long *ret)
        return 1;
 }
 
+static int git_parse_ssize_t(const char *value, ssize_t *ret)
+{
+       intmax_t tmp;
+       if (!git_parse_signed(value, &tmp, maximum_signed_value_of_type(ssize_t)))
+               return 0;
+       *ret = tmp;
+       return 1;
+}
+
 NORETURN
 static void die_bad_number(const char *name, const char *value)
 {
@@ -800,6 +901,14 @@ unsigned long git_config_ulong(const char *name, const char *value)
        return ret;
 }
 
+ssize_t git_config_ssize_t(const char *name, const char *value)
+{
+       ssize_t ret;
+       if (!git_parse_ssize_t(value, &ret))
+               die_bad_number(name, value);
+       return ret;
+}
+
 int git_parse_maybe_bool(const char *value)
 {
        if (!value)
@@ -856,7 +965,7 @@ int git_config_pathname(const char **dest, const char *var, const char *value)
 {
        if (!value)
                return config_error_nonbool(var);
-       *dest = expand_user_path(value);
+       *dest = expand_user_path(value, 0);
        if (!*dest)
                die(_("failed to expand user dir in: '%s'"), value);
        return 0;
@@ -1406,7 +1515,7 @@ static int do_git_config_sequence(config_fn_t fn, void *data)
 {
        int ret = 0;
        char *xdg_config = xdg_config_home("config");
-       char *user_config = expand_user_path("~/.gitconfig");
+       char *user_config = expand_user_path("~/.gitconfig", 0);
        char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;
 
        current_parsing_scope = CONFIG_SCOPE_SYSTEM;
@@ -1503,6 +1612,31 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
        }
 }
 
+void read_early_config(config_fn_t cb, void *data)
+{
+       struct strbuf buf = STRBUF_INIT;
+
+       git_config_with_options(cb, data, NULL, 1);
+
+       /*
+        * When setup_git_directory() was not yet asked to discover the
+        * GIT_DIR, we ask discover_git_directory() to figure out whether there
+        * is any repository config we should use (but unlike
+        * setup_git_directory_gently(), no global state is changed, most
+        * notably, the current working directory is still the same after the
+        * call).
+        */
+       if (!have_git_dir() && discover_git_directory(&buf)) {
+               struct git_config_source repo_config;
+
+               memset(&repo_config, 0, sizeof(repo_config));
+               strbuf_addstr(&buf, "/config");
+               repo_config.file = buf.buf;
+               git_config_with_options(cb, data, &repo_config, 1);
+       }
+       strbuf_release(&buf);
+}
+
 static void git_config_check_init(void);
 
 void git_config(config_fn_t fn, void *data)
index 7d65c1c73634363e85a652619cc8705223bc70cb..568a35f754e41d54dc0e7133bbfaa0d1d02ccb27 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -111,8 +111,8 @@ static void annotate_refs_with_symref_info(struct ref *ref)
  */
 struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                              struct ref **list, unsigned int flags,
-                             struct sha1_array *extra_have,
-                             struct sha1_array *shallow_points)
+                             struct oid_array *extra_have,
+                             struct oid_array *shallow_points)
 {
        struct ref **orig_list = list;
 
@@ -153,7 +153,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                                die("protocol error: expected shallow sha-1, got '%s'", arg);
                        if (!shallow_points)
                                die("repository on the other end cannot be shallow");
-                       sha1_array_append(shallow_points, old_oid.hash);
+                       oid_array_append(shallow_points, &old_oid);
                        continue;
                }
 
@@ -169,7 +169,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                }
 
                if (extra_have && !strcmp(name, ".have")) {
-                       sha1_array_append(extra_have, old_oid.hash);
+                       oid_array_append(extra_have, &old_oid);
                        continue;
                }
 
@@ -730,7 +730,7 @@ static void handle_ssh_variant(const char *ssh_command, int is_cmdline,
                const char **ssh_argv;
 
                p = xstrdup(ssh_command);
-               if (split_cmdline(p, &ssh_argv)) {
+               if (split_cmdline(p, &ssh_argv) > 0) {
                        variant = basename((char *)ssh_argv[0]);
                        /*
                         * At this point, variant points into the buffer
index fc32286a43cdece9ea468c4a07c2a8e3d585f1b5..1150164d5ce0e7396191a192189b43444883f739 100644 (file)
@@ -213,6 +213,20 @@ _get_comp_words_by_ref ()
 }
 fi
 
+# Fills the COMPREPLY array with prefiltered words without any additional
+# processing.
+# Callers must take care of providing only words that match the current word
+# to be completed and adding any prefix and/or suffix (trailing space!), if
+# necessary.
+# 1: List of newline-separated matching completion words, complete with
+#    prefix and suffix.
+__gitcomp_direct ()
+{
+       local IFS=$'\n'
+
+       COMPREPLY=($1)
+}
+
 __gitcompappend ()
 {
        local x i=${#COMPREPLY[@]}
@@ -338,14 +352,27 @@ __git_index_files ()
        done | sort | uniq
 }
 
+# Lists branches from the local repository.
+# 1: A prefix to be added to each listed branch (optional).
+# 2: List only branches matching this word (optional; list all branches if
+#    unset or empty).
+# 3: A suffix to be appended to each listed branch (optional).
 __git_heads ()
 {
-       __git for-each-ref --format='%(refname:short)' refs/heads
+       local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+
+       __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       "refs/heads/$cur_*" "refs/heads/$cur_*/**"
 }
 
+# Lists tags from the local repository.
+# Accepts the same positional parameters as __git_heads() above.
 __git_tags ()
 {
-       __git for-each-ref --format='%(refname:short)' refs/tags
+       local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+
+       __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       "refs/tags/$cur_*" "refs/tags/$cur_*/**"
 }
 
 # Lists refs from the local (by default) or from a remote repository.
@@ -354,11 +381,21 @@ __git_tags ()
 #    Can be the name of a configured remote, a path, or a URL.
 # 2: In addition to local refs, list unique branches from refs/remotes/ for
 #    'git checkout's tracking DWIMery (optional; ignored, if set but empty).
+# 3: A prefix to be added to each listed ref (optional).
+# 4: List only refs matching this word (optional; list all refs if unset or
+#    empty).
+# 5: A suffix to be appended to each listed ref (optional; ignored, if set
+#    but empty).
+#
+# Use __git_complete_refs() instead.
 __git_refs ()
 {
        local i hash dir track="${2-}"
        local list_refs_from=path remote="${1-}"
-       local format refs pfx
+       local format refs
+       local pfx="${3-}" cur_="${4-$cur}" sfx="${5-}"
+       local match="${4-}"
+       local fer_pfx="${pfx//\%/%%}" # "escape" for-each-ref format specifiers
 
        __git_find_repo_path
        dir="$__git_repo_path"
@@ -382,63 +419,78 @@ __git_refs ()
        fi
 
        if [ "$list_refs_from" = path ]; then
-               case "$cur" in
+               if [[ "$cur_" == ^* ]]; then
+                       pfx="$pfx^"
+                       fer_pfx="$fer_pfx^"
+                       cur_=${cur_#^}
+                       match=${match#^}
+               fi
+               case "$cur_" in
                refs|refs/*)
                        format="refname"
-                       refs="${cur%/*}"
+                       refs=("$match*" "$match*/**")
                        track=""
                        ;;
                *)
-                       [[ "$cur" == ^* ]] && pfx="^"
                        for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
-                               if [ -e "$dir/$i" ]; then echo $pfx$i; fi
+                               case "$i" in
+                               $match*)
+                                       if [ -e "$dir/$i" ]; then
+                                               echo "$pfx$i$sfx"
+                                       fi
+                                       ;;
+                               esac
                        done
-                       format="refname:short"
-                       refs="refs/tags refs/heads refs/remotes"
+                       format="refname:strip=2"
+                       refs=("refs/tags/$match*" "refs/tags/$match*/**"
+                               "refs/heads/$match*" "refs/heads/$match*/**"
+                               "refs/remotes/$match*" "refs/remotes/$match*/**")
                        ;;
                esac
-               __git_dir="$dir" __git for-each-ref --format="$pfx%($format)" \
-                       $refs
+               __git_dir="$dir" __git for-each-ref --format="$fer_pfx%($format)$sfx" \
+                       "${refs[@]}"
                if [ -n "$track" ]; then
                        # employ the heuristic used by git checkout
                        # Try to find a remote branch that matches the completion word
                        # but only output if the branch name is unique
-                       local ref entry
-                       __git for-each-ref --shell --format="ref=%(refname:short)" \
-                               "refs/remotes/" | \
-                       while read -r entry; do
-                               eval "$entry"
-                               ref="${ref#*/}"
-                               if [[ "$ref" == "$cur"* ]]; then
-                                       echo "$ref"
-                               fi
-                       done | sort | uniq -u
+                       __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+                               --sort="refname:strip=3" \
+                               "refs/remotes/*/$match*" "refs/remotes/*/$match*/**" | \
+                       uniq -u
                fi
                return
        fi
-       case "$cur" in
+       case "$cur_" in
        refs|refs/*)
-               __git ls-remote "$remote" "$cur*" | \
+               __git ls-remote "$remote" "$match*" | \
                while read -r hash i; do
                        case "$i" in
                        *^{}) ;;
-                       *) echo "$i" ;;
+                       *) echo "$pfx$i$sfx" ;;
                        esac
                done
                ;;
        *)
                if [ "$list_refs_from" = remote ]; then
-                       echo "HEAD"
-                       __git for-each-ref --format="%(refname:short)" \
-                               "refs/remotes/$remote/" | sed -e "s#^$remote/##"
+                       case "HEAD" in
+                       $match*)        echo "${pfx}HEAD$sfx" ;;
+                       esac
+                       __git for-each-ref --format="$fer_pfx%(refname:strip=3)$sfx" \
+                               "refs/remotes/$remote/$match*" \
+                               "refs/remotes/$remote/$match*/**"
                else
-                       __git ls-remote "$remote" HEAD \
-                               "refs/tags/*" "refs/heads/*" "refs/remotes/*" |
+                       local query_symref
+                       case "HEAD" in
+                       $match*)        query_symref="HEAD" ;;
+                       esac
+                       __git ls-remote "$remote" $query_symref \
+                               "refs/tags/$match*" "refs/heads/$match*" \
+                               "refs/remotes/$match*" |
                        while read -r hash i; do
                                case "$i" in
                                *^{})   ;;
-                               refs/*) echo "${i#refs/*/}" ;;
-                               *)      echo "$i" ;;  # symbolic refs
+                               refs/*) echo "$pfx${i#refs/*/}$sfx" ;;
+                               *)      echo "$pfx$i$sfx" ;;  # symbolic refs
                                esac
                        done
                fi
@@ -446,7 +498,38 @@ __git_refs ()
        esac
 }
 
+# Completes refs, short and long, local and remote, symbolic and pseudo.
+#
+# Usage: __git_complete_refs [<option>]...
+# --remote=<remote>: The remote to list refs from, can be the name of a
+#                    configured remote, a path, or a URL.
+# --track: List unique remote branches for 'git checkout's tracking DWIMery.
+# --pfx=<prefix>: A prefix to be added to each ref.
+# --cur=<word>: The current ref to be completed.  Defaults to the current
+#               word to be completed.
+# --sfx=<suffix>: A suffix to be appended to each ref instead of the default
+#                 space.
+__git_complete_refs ()
+{
+       local remote track pfx cur_="$cur" sfx=" "
+
+       while test $# != 0; do
+               case "$1" in
+               --remote=*)     remote="${1##--remote=}" ;;
+               --track)        track="yes" ;;
+               --pfx=*)        pfx="${1##--pfx=}" ;;
+               --cur=*)        cur_="${1##--cur=}" ;;
+               --sfx=*)        sfx="${1##--sfx=}" ;;
+               *)              return 1 ;;
+               esac
+               shift
+       done
+
+       __gitcomp_direct "$(__git_refs "$remote" "$track" "$pfx" "$cur_" "$sfx")"
+}
+
 # __git_refs2 requires 1 argument (to pass to __git_refs)
+# Deprecated: use __git_complete_fetch_refspecs() instead.
 __git_refs2 ()
 {
        local i
@@ -455,6 +538,24 @@ __git_refs2 ()
        done
 }
 
+# Completes refspecs for fetching from a remote repository.
+# 1: The remote repository.
+# 2: A prefix to be added to each listed refspec (optional).
+# 3: The ref to be completed as a refspec instead of the current word to be
+#    completed (optional)
+# 4: A suffix to be appended to each listed refspec instead of the default
+#    space (optional).
+__git_complete_fetch_refspecs ()
+{
+       local i remote="$1" pfx="${2-}" cur_="${3-$cur}" sfx="${4- }"
+
+       __gitcomp_direct "$(
+               for i in $(__git_refs "$remote" "" "" "$cur_") ; do
+                       echo "$pfx$i:$i$sfx"
+               done
+               )"
+}
+
 # __git_refs_remotes requires 1 argument (to pass to ls-remote)
 __git_refs_remotes ()
 {
@@ -554,15 +655,15 @@ __git_complete_revlist_file ()
        *...*)
                pfx="${cur_%...*}..."
                cur_="${cur_#*...}"
-               __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+               __git_complete_refs --pfx="$pfx" --cur="$cur_"
                ;;
        *..*)
                pfx="${cur_%..*}.."
                cur_="${cur_#*..}"
-               __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+               __git_complete_refs --pfx="$pfx" --cur="$cur_"
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        esac
 }
@@ -647,23 +748,23 @@ __git_complete_remote_or_refspec ()
        case "$cmd" in
        fetch)
                if [ $lhs = 1 ]; then
-                       __gitcomp_nl "$(__git_refs2 "$remote")" "$pfx" "$cur_"
+                       __git_complete_fetch_refspecs "$remote" "$pfx" "$cur_"
                else
-                       __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+                       __git_complete_refs --pfx="$pfx" --cur="$cur_"
                fi
                ;;
        pull|remote)
                if [ $lhs = 1 ]; then
-                       __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
+                       __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_"
                else
-                       __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+                       __git_complete_refs --pfx="$pfx" --cur="$cur_"
                fi
                ;;
        push)
                if [ $lhs = 1 ]; then
-                       __gitcomp_nl "$(__git_refs)" "$pfx" "$cur_"
+                       __git_complete_refs --pfx="$pfx" --cur="$cur_"
                else
-                       __gitcomp_nl "$(__git_refs "$remote")" "$pfx" "$cur_"
+                       __git_complete_refs --remote="$remote" --pfx="$pfx" --cur="$cur_"
                fi
                ;;
        esac
@@ -1066,7 +1167,7 @@ _git_bisect ()
 
        case "$subcommand" in
        bad|good|reset|skip|start)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        *)
                ;;
@@ -1088,12 +1189,12 @@ _git_branch ()
 
        case "$cur" in
        --set-upstream-to=*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur##--set-upstream-to=}"
+               __git_complete_refs --cur="${cur##--set-upstream-to=}"
                ;;
        --*)
                __gitcomp "
                        --color --no-color --verbose --abbrev= --no-abbrev
-                       --track --no-track --contains --merged --no-merged
+                       --track --no-track --contains --no-contains --merged --no-merged
                        --set-upstream-to= --edit-description --list
                        --unset-upstream --delete --move --remotes
                        --column --no-column --sort= --points-at
@@ -1101,9 +1202,9 @@ _git_branch ()
                ;;
        *)
                if [ $only_local_ref = "y" -a $has_r = "n" ]; then
-                       __gitcomp_nl "$(__git_heads)"
+                       __gitcomp_direct "$(__git_heads "" "$cur" " ")"
                else
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                fi
                ;;
        esac
@@ -1146,18 +1247,18 @@ _git_checkout ()
        *)
                # check if --track, --no-track, or --no-guess was specified
                # if so, disable DWIM mode
-               local flags="--track --no-track --no-guess" track=1
+               local flags="--track --no-track --no-guess" track_opt="--track"
                if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
-                       track=''
+                       track_opt=''
                fi
-               __gitcomp_nl "$(__git_refs '' $track)"
+               __git_complete_refs $track_opt
                ;;
        esac
 }
 
 _git_cherry ()
 {
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_cherry_pick ()
@@ -1172,7 +1273,7 @@ _git_cherry_pick ()
                __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        esac
 }
@@ -1224,7 +1325,7 @@ _git_commit ()
 {
        case "$prev" in
        -c|-C)
-               __gitcomp_nl "$(__git_refs)" "" "${cur}"
+               __git_complete_refs
                return
                ;;
        esac
@@ -1237,7 +1338,7 @@ _git_commit ()
                ;;
        --reuse-message=*|--reedit-message=*|\
        --fixup=*|--squash=*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+               __git_complete_refs --cur="${cur#*=}"
                return
                ;;
        --untracked-files=*)
@@ -1277,7 +1378,7 @@ _git_describe ()
                        "
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 __git_diff_algorithms="myers minimal patience histogram"
@@ -1428,8 +1529,43 @@ _git_gitk ()
        _gitk
 }
 
-__git_match_ctag() {
-       awk "/^${1//\//\\/}/ { print \$1 }" "$2"
+# Lists matching symbol names from a tag (as in ctags) file.
+# 1: List symbol names matching this word.
+# 2: The tag file to list symbol names from.
+# 3: A prefix to be added to each listed symbol name (optional).
+# 4: A suffix to be appended to each listed symbol name (optional).
+__git_match_ctag () {
+       awk -v pfx="${3-}" -v sfx="${4-}" "
+               /^${1//\//\\/}/ { print pfx \$1 sfx }
+               " "$2"
+}
+
+# Complete symbol names from a tag file.
+# Usage: __git_complete_symbol [<option>]...
+# --tags=<file>: The tag file to list symbol names from instead of the
+#                default "tags".
+# --pfx=<prefix>: A prefix to be added to each symbol name.
+# --cur=<word>: The current symbol name to be completed.  Defaults to
+#               the current word to be completed.
+# --sfx=<suffix>: A suffix to be appended to each symbol name instead
+#                 of the default space.
+__git_complete_symbol () {
+       local tags=tags pfx="" cur_="${cur-}" sfx=" "
+
+       while test $# != 0; do
+               case "$1" in
+               --tags=*)       tags="${1##--tags=}" ;;
+               --pfx=*)        pfx="${1##--pfx=}" ;;
+               --cur=*)        cur_="${1##--cur=}" ;;
+               --sfx=*)        sfx="${1##--sfx=}" ;;
+               *)              return 1 ;;
+               esac
+               shift
+       done
+
+       if test -r "$tags"; then
+               __gitcomp_direct "$(__git_match_ctag "$cur_" "$tags" "$pfx" "$sfx")"
+       fi
 }
 
 _git_grep ()
@@ -1459,14 +1595,11 @@ _git_grep ()
 
        case "$cword,$prev" in
        2,*|*,-*)
-               if test -r tags; then
-                       __gitcomp_nl "$(__git_match_ctag "$cur" tags)"
-                       return
-               fi
+               __git_complete_symbol && return
                ;;
        esac
 
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_help ()
@@ -1573,6 +1706,19 @@ _git_log ()
        if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
                merge="--merge"
        fi
+       case "$prev,$cur" in
+       -L,:*:*)
+               return  # fall back to Bash filename completion
+               ;;
+       -L,:*)
+               __git_complete_symbol --cur="${cur#:}" --sfx=":"
+               return
+               ;;
+       -G,*|-S,*)
+               __git_complete_symbol
+               return
+               ;;
+       esac
        case "$cur" in
        --pretty=*|--format=*)
                __gitcomp "$__git_log_pretty_formats $(__git_pretty_aliases)
@@ -1618,6 +1764,21 @@ _git_log ()
                        "
                return
                ;;
+       -L:*:*)
+               return  # fall back to Bash filename completion
+               ;;
+       -L:*)
+               __git_complete_symbol --cur="${cur#-L:}" --sfx=":"
+               return
+               ;;
+       -G*)
+               __git_complete_symbol --pfx="-G" --cur="${cur#-G}"
+               return
+               ;;
+       -S*)
+               __git_complete_symbol --pfx="-S" --cur="${cur#-S}"
+               return
+               ;;
        esac
        __git_complete_revlist
 }
@@ -1640,7 +1801,7 @@ _git_merge ()
                        --rerere-autoupdate --no-rerere-autoupdate --abort --continue"
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_mergetool ()
@@ -1665,7 +1826,7 @@ _git_merge_base ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_mv ()
@@ -1703,7 +1864,7 @@ _git_notes ()
        ,*)
                case "$prev" in
                --ref)
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                        ;;
                *)
                        __gitcomp "$subcommands --ref"
@@ -1712,7 +1873,7 @@ _git_notes ()
                ;;
        add,--reuse-message=*|append,--reuse-message=*|\
        add,--reedit-message=*|append,--reedit-message=*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+               __git_complete_refs --cur="${cur#*=}"
                ;;
        add,--*|append,--*)
                __gitcomp '--file= --message= --reedit-message=
@@ -1731,7 +1892,7 @@ _git_notes ()
                -m|-F)
                        ;;
                *)
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                        ;;
                esac
                ;;
@@ -1769,10 +1930,10 @@ __git_complete_force_with_lease ()
        --*=)
                ;;
        *:*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur_#*:}"
+               __git_complete_refs --cur="${cur_#*:}"
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)" "" "$cur_"
+               __git_complete_refs --cur="$cur_"
                ;;
        esac
 }
@@ -1848,7 +2009,7 @@ _git_rebase ()
 
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_reflog ()
@@ -1859,7 +2020,7 @@ _git_reflog ()
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
        fi
 }
 
@@ -2005,7 +2166,7 @@ _git_config ()
                return
                ;;
        branch.*.merge)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                return
                ;;
        branch.*.rebase)
@@ -2109,7 +2270,7 @@ _git_config ()
                ;;
        branch.*)
                local pfx="${cur%.*}." cur_="${cur#*.}"
-               __gitcomp_nl "$(__git_heads)" "$pfx" "$cur_" "."
+               __gitcomp_direct "$(__git_heads "$pfx" "$cur_" ".")"
                __gitcomp_nl_append $'autosetupmerge\nautosetuprebase\n' "$pfx" "$cur_"
                return
                ;;
@@ -2516,7 +2677,7 @@ _git_replace ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_rerere ()
@@ -2540,7 +2701,7 @@ _git_reset ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_revert ()
@@ -2559,7 +2720,7 @@ _git_revert ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_rm ()
@@ -2667,7 +2828,7 @@ _git_stash ()
                        ;;
                branch,*)
                        if [ $cword -eq 3 ]; then
-                               __gitcomp_nl "$(__git_refs)";
+                               __git_complete_refs
                        else
                                __gitcomp_nl "$(__git stash list \
                                                | sed -n -e 's/:.*//p')"
@@ -2834,7 +2995,7 @@ _git_tag ()
                i="${words[c]}"
                case "$i" in
                -d|-v)
-                       __gitcomp_nl "$(__git_tags)"
+                       __gitcomp_direct "$(__git_tags "" "$cur" " ")"
                        return
                        ;;
                -f)
@@ -2849,11 +3010,11 @@ _git_tag ()
                ;;
        -*|tag)
                if [ $f = 1 ]; then
-                       __gitcomp_nl "$(__git_tags)"
+                       __gitcomp_direct "$(__git_tags "" "$cur" " ")"
                fi
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        esac
 
@@ -2862,7 +3023,7 @@ _git_tag ()
                __gitcomp "
                        --list --delete --verify --annotate --message --file
                        --sign --cleanup --local-user --force --column --sort=
-                       --contains --points-at --merged --no-merged --create-reflog
+                       --contains --no-contains --points-at --merged --no-merged --create-reflog
                        "
                ;;
        esac
@@ -3024,6 +3185,15 @@ if [[ -n ${ZSH_VERSION-} ]]; then
                esac
        }
 
+       __gitcomp_direct ()
+       {
+               emulate -L zsh
+
+               local IFS=$'\n'
+               compset -P '*[=:]'
+               compadd -Q -- ${=1} && _ret=0
+       }
+
        __gitcomp_nl ()
        {
                emulate -L zsh
index e25541308a1c269285a26b205d2d9990a789060a..c3521fbfc44fd8db18244bc10d346ee39b1c3c90 100644 (file)
@@ -67,6 +67,15 @@ __gitcomp ()
        esac
 }
 
+__gitcomp_direct ()
+{
+       emulate -L zsh
+
+       local IFS=$'\n'
+       compset -P '*[=:]'
+       compadd -Q -- ${=1} && _ret=0
+}
+
 __gitcomp_nl ()
 {
        emulate -L zsh
index 97eacd7832b28377c5e14918ce6bededc55d7d40..c6cbef38c2a580555dfcdc67e04f6ac507f9066c 100644 (file)
@@ -82,6 +82,7 @@
 #     contains      relative to newer annotated tag (v1.6.3.2~35)
 #     branch        relative to newer tag or branch (master~4)
 #     describe      relative to older annotated tag (v1.6.3.1-13-gdd42c2f)
+#     tag           relative to any older tag (v1.6.3.1-13-gdd42c2f)
 #     default       exactly matching tag
 #
 # If you would like a colored hint about the current dirty state, set
@@ -443,6 +444,8 @@ __git_ps1 ()
                                        git describe --contains HEAD ;;
                                (branch)
                                        git describe --contains --all HEAD ;;
+                               (tag)
+                                       git describe --tags HEAD ;;
                                (describe)
                                        git describe HEAD ;;
                                (* | default)
index d7e97bbc76c27f61e83a5328831fd6889b22e662..8c171dd959f69501706c565218665ef474252d43 100755 (executable)
@@ -26,13 +26,13 @@ n,dry-run            don't recreate the branch"
 . git-sh-setup
 
 search_reflog () {
-        sed -ne 's~^\([^ ]*\) .*\tcheckout: moving from '"$1"' .*~\1~p' \
+       sed -ne 's~^\([^ ]*\) .*        checkout: moving from '"$1"' .*~\1~p' \
                 < "$GIT_DIR"/logs/HEAD
 }
 
 search_reflog_merges () {
        git rev-parse $(
-               sed -ne 's~^[^ ]* \([^ ]*\) .*\tmerge '"$1"':.*~\1^2~p' \
+               sed -ne 's~^[^ ]* \([^ ]*\) .*  merge '"$1"':.*~\1^2~p' \
                        < "$GIT_DIR"/logs/HEAD
        )
 }
index cc8a6ee19214b12758fc3d4b39ff4a07f4442d91..91550bfb0b3325ad92a3f3c65d5f779b61fa9951 100644 (file)
@@ -83,6 +83,19 @@ static void do_cache(const char *socket, const char *action, int timeout,
        strbuf_release(&buf);
 }
 
+static char *get_socket_path(void)
+{
+       struct stat sb;
+       char *old_dir, *socket;
+       old_dir = expand_user_path("~/.git-credential-cache", 0);
+       if (old_dir && !stat(old_dir, &sb) && S_ISDIR(sb.st_mode))
+               socket = xstrfmt("%s/socket", old_dir);
+       else
+               socket = xdg_cache_home("credential/socket");
+       free(old_dir);
+       return socket;
+}
+
 int cmd_main(int argc, const char **argv)
 {
        char *socket_path = NULL;
@@ -106,7 +119,7 @@ int cmd_main(int argc, const char **argv)
        op = argv[0];
 
        if (!socket_path)
-               socket_path = expand_user_path("~/.git-credential-cache/socket");
+               socket_path = get_socket_path();
        if (!socket_path)
                die("unable to find a suitable socket path; use --socket");
 
index 55ca1b1334319924dcbbf69ec39b2335e6a452aa..ac295420dd0d03d1b31922f9b7c16a83e987e6a3 100644 (file)
@@ -168,7 +168,7 @@ int cmd_main(int argc, const char **argv)
        if (file) {
                string_list_append(&fns, file);
        } else {
-               if ((file = expand_user_path("~/.git-credentials")))
+               if ((file = expand_user_path("~/.git-credentials", 0)))
                        string_list_append_nodup(&fns, file);
                file = xdg_config_home("credentials");
                if (file)
index 473e6b6b63c42e59d5a89985b2573ab5c2815157..ac7181a4832672ef1e599324a15868d1b06f8fc0 100644 (file)
--- a/daemon.c
+++ b/daemon.c
@@ -4,10 +4,6 @@
 #include "strbuf.h"
 #include "string-list.h"
 
-#ifndef HOST_NAME_MAX
-#define HOST_NAME_MAX 256
-#endif
-
 #ifdef NO_INITGROUPS
 #define initgroups(x, y) (0) /* nothing */
 #endif
@@ -449,46 +445,42 @@ static void copy_to_log(int fd)
        fclose(fp);
 }
 
-static int run_service_command(const char **argv)
+static int run_service_command(struct child_process *cld)
 {
-       struct child_process cld = CHILD_PROCESS_INIT;
-
-       cld.argv = argv;
-       cld.git_cmd = 1;
-       cld.err = -1;
-       if (start_command(&cld))
+       argv_array_push(&cld->args, ".");
+       cld->git_cmd = 1;
+       cld->err = -1;
+       if (start_command(cld))
                return -1;
 
        close(0);
        close(1);
 
-       copy_to_log(cld.err);
+       copy_to_log(cld->err);
 
-       return finish_command(&cld);
+       return finish_command(cld);
 }
 
 static int upload_pack(void)
 {
-       /* Timeout as string */
-       char timeout_buf[64];
-       const char *argv[] = { "upload-pack", "--strict", NULL, ".", NULL };
-
-       argv[2] = timeout_buf;
-
-       snprintf(timeout_buf, sizeof timeout_buf, "--timeout=%u", timeout);
-       return run_service_command(argv);
+       struct child_process cld = CHILD_PROCESS_INIT;
+       argv_array_pushl(&cld.args, "upload-pack", "--strict", NULL);
+       argv_array_pushf(&cld.args, "--timeout=%u", timeout);
+       return run_service_command(&cld);
 }
 
 static int upload_archive(void)
 {
-       static const char *argv[] = { "upload-archive", ".", NULL };
-       return run_service_command(argv);
+       struct child_process cld = CHILD_PROCESS_INIT;
+       argv_array_push(&cld.args, "upload-archive");
+       return run_service_command(&cld);
 }
 
 static int receive_pack(void)
 {
-       static const char *argv[] = { "receive-pack", ".", NULL };
-       return run_service_command(argv);
+       struct child_process cld = CHILD_PROCESS_INIT;
+       argv_array_push(&cld.args, "receive-pack");
+       return run_service_command(&cld);
 }
 
 static struct daemon_service daemon_service[] = {
index df762fd0f7d1ec74175a42de07b064107d3a601a..79229382b06cefc98aa8926065f0fb999ad1da2e 100644 (file)
@@ -236,7 +236,7 @@ static void fixup_paths(const char **path, struct strbuf *replacement)
 void diff_no_index(struct rev_info *revs,
                   int argc, const char **argv)
 {
-       int i, prefixlen;
+       int i;
        const char *paths[2];
        struct strbuf replacement = STRBUF_INIT;
        const char *prefix = revs->prefix;
@@ -257,7 +257,6 @@ void diff_no_index(struct rev_info *revs,
                }
        }
 
-       prefixlen = prefix ? strlen(prefix) : 0;
        for (i = 0; i < 2; i++) {
                const char *p = argv[argc - 2 + i];
                if (!strcmp(p, "-"))
@@ -266,8 +265,8 @@ void diff_no_index(struct rev_info *revs,
                         * path that is "-", spell it as "./-".
                         */
                        p = file_from_standard_input;
-               else if (prefixlen)
-                       p = xstrdup(prefix_filename(prefix, prefixlen, p));
+               else if (prefix)
+                       p = prefix_filename(prefix, p);
                paths[i] = p;
        }
 
diff --git a/diff.c b/diff.c
index a628ac3a95108ca79aef0b41b7051ab2fcbe693a..11eef1c85d6d766320bfa24b10483fbc0869efe1 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -398,7 +398,7 @@ static struct diff_tempfile {
         */
        const char *name;
 
-       char hex[GIT_SHA1_HEXSZ + 1];
+       char hex[GIT_MAX_HEXSZ + 1];
        char mode[10];
 
        /*
@@ -4023,8 +4023,7 @@ int diff_opt_parse(struct diff_options *options,
        else if (!strcmp(arg, "--pickaxe-regex"))
                options->pickaxe_opts |= DIFF_PICKAXE_REGEX;
        else if ((argcount = short_opt('O', av, &optarg))) {
-               const char *path = prefix_filename(prefix, strlen(prefix), optarg);
-               options->orderfile = xstrdup(path);
+               options->orderfile = prefix_filename(prefix, optarg);
                return argcount;
        }
        else if ((argcount = parse_long_opt("diff-filter", av, &optarg))) {
@@ -4071,13 +4070,14 @@ int diff_opt_parse(struct diff_options *options,
        else if (!strcmp(arg, "--no-function-context"))
                DIFF_OPT_CLR(options, FUNCCONTEXT);
        else if ((argcount = parse_long_opt("output", av, &optarg))) {
-               const char *path = prefix_filename(prefix, strlen(prefix), optarg);
+               char *path = prefix_filename(prefix, optarg);
                options->file = fopen(path, "w");
                if (!options->file)
                        die_errno("Could not open '%s'", path);
                options->close_file = 1;
                if (options->use_color != GIT_COLOR_ALWAYS)
                        options->use_color = GIT_COLOR_NEVER;
+               free(path);
                return argcount;
        } else
                return 0;
@@ -4219,7 +4219,7 @@ const char *diff_aligned_abbrev(const struct object_id *oid, int len)
         * uniqueness across all objects (statistically speaking).
         */
        if (abblen < GIT_SHA1_HEXSZ - 3) {
-               static char hex[GIT_SHA1_HEXSZ + 1];
+               static char hex[GIT_MAX_HEXSZ + 1];
                if (len < abblen && abblen <= len + 2)
                        xsnprintf(hex, sizeof(hex), "%s%.*s", abbrev, len+3-abblen, "..");
                else
@@ -4570,6 +4570,19 @@ static void patch_id_consume(void *priv, char *line, unsigned long len)
        data->patchlen += new_len;
 }
 
+static void patch_id_add_string(git_SHA_CTX *ctx, const char *str)
+{
+       git_SHA1_Update(ctx, str, strlen(str));
+}
+
+static void patch_id_add_mode(git_SHA_CTX *ctx, unsigned mode)
+{
+       /* large enough for 2^32 in octal */
+       char buf[12];
+       int len = xsnprintf(buf, sizeof(buf), "%06o", mode);
+       git_SHA1_Update(ctx, buf, len);
+}
+
 /* returns 0 upon success, and writes result into sha1 */
 static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1, int diff_header_only)
 {
@@ -4577,7 +4590,6 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1,
        int i;
        git_SHA_CTX ctx;
        struct patch_id_t data;
-       char buffer[PATH_MAX * 4 + 20];
 
        git_SHA1_Init(&ctx);
        memset(&data, 0, sizeof(struct patch_id_t));
@@ -4609,36 +4621,30 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1,
 
                len1 = remove_space(p->one->path, strlen(p->one->path));
                len2 = remove_space(p->two->path, strlen(p->two->path));
-               if (p->one->mode == 0)
-                       len1 = snprintf(buffer, sizeof(buffer),
-                                       "diff--gita/%.*sb/%.*s"
-                                       "newfilemode%06o"
-                                       "---/dev/null"
-                                       "+++b/%.*s",
-                                       len1, p->one->path,
-                                       len2, p->two->path,
-                                       p->two->mode,
-                                       len2, p->two->path);
-               else if (p->two->mode == 0)
-                       len1 = snprintf(buffer, sizeof(buffer),
-                                       "diff--gita/%.*sb/%.*s"
-                                       "deletedfilemode%06o"
-                                       "---a/%.*s"
-                                       "+++/dev/null",
-                                       len1, p->one->path,
-                                       len2, p->two->path,
-                                       p->one->mode,
-                                       len1, p->one->path);
-               else
-                       len1 = snprintf(buffer, sizeof(buffer),
-                                       "diff--gita/%.*sb/%.*s"
-                                       "---a/%.*s"
-                                       "+++b/%.*s",
-                                       len1, p->one->path,
-                                       len2, p->two->path,
-                                       len1, p->one->path,
-                                       len2, p->two->path);
-               git_SHA1_Update(&ctx, buffer, len1);
+               patch_id_add_string(&ctx, "diff--git");
+               patch_id_add_string(&ctx, "a/");
+               git_SHA1_Update(&ctx, p->one->path, len1);
+               patch_id_add_string(&ctx, "b/");
+               git_SHA1_Update(&ctx, p->two->path, len2);
+
+               if (p->one->mode == 0) {
+                       patch_id_add_string(&ctx, "newfilemode");
+                       patch_id_add_mode(&ctx, p->two->mode);
+                       patch_id_add_string(&ctx, "---/dev/null");
+                       patch_id_add_string(&ctx, "+++b/");
+                       git_SHA1_Update(&ctx, p->two->path, len2);
+               } else if (p->two->mode == 0) {
+                       patch_id_add_string(&ctx, "deletedfilemode");
+                       patch_id_add_mode(&ctx, p->one->mode);
+                       patch_id_add_string(&ctx, "---a/");
+                       git_SHA1_Update(&ctx, p->one->path, len1);
+                       patch_id_add_string(&ctx, "+++/dev/null");
+               } else {
+                       patch_id_add_string(&ctx, "---a/");
+                       git_SHA1_Update(&ctx, p->one->path, len1);
+                       patch_id_add_string(&ctx, "+++b/");
+                       git_SHA1_Update(&ctx, p->two->path, len2);
+               }
 
                if (diff_header_only)
                        continue;
diff --git a/diff.h b/diff.h
index e9ccb38c26c7f1b5b0d5932bb98c2875276cf828..5be1ee77a759f330db4ace64a5772f23e3d19d50 100644 (file)
--- a/diff.h
+++ b/diff.h
@@ -14,7 +14,7 @@ struct diff_queue_struct;
 struct strbuf;
 struct diff_filespec;
 struct userdiff_driver;
-struct sha1_array;
+struct oid_array;
 struct commit;
 struct combine_diff_path;
 
@@ -236,7 +236,7 @@ struct combine_diff_path {
 extern void show_combined_diff(struct combine_diff_path *elem, int num_parent,
                              int dense, struct rev_info *);
 
-extern void diff_tree_combined(const unsigned char *sha1, const struct sha1_array *parents, int dense, struct rev_info *rev);
+extern void diff_tree_combined(const unsigned char *sha1, const struct oid_array *parents, int dense, struct rev_info *rev);
 
 extern void diff_tree_combined_merge(const struct commit *commit, int dense, struct rev_info *rev);
 
index 9795ca1c159a5177b1b7031a87c8f8bd5e5be3d5..341529b5a865ae6460f50d8593d52497059f2408 100644 (file)
@@ -81,12 +81,15 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
                regmatch_t regmatch;
                int flags = 0;
 
-               while (*data &&
+               while (sz && *data &&
                       !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
                        flags |= REG_NOTBOL;
                        data += regmatch.rm_eo;
-                       if (*data && regmatch.rm_so == regmatch.rm_eo)
+                       sz -= regmatch.rm_eo;
+                       if (sz && *data && regmatch.rm_so == regmatch.rm_eo) {
                                data++;
+                               sz--;
+                       }
                        cnt++;
                }
 
diff --git a/dir.c b/dir.c
index 837ff965a470e74028fce6019f4584e55dcf445c..f451bfa48c0a0e7905d4c2adf4e3e05a8d272a8a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -2765,23 +2765,33 @@ void untracked_cache_add_to_index(struct index_state *istate,
 /* Update gitfile and core.worktree setting to connect work tree and git dir */
 void connect_work_tree_and_git_dir(const char *work_tree_, const char *git_dir_)
 {
-       struct strbuf file_name = STRBUF_INIT;
+       struct strbuf gitfile_sb = STRBUF_INIT;
+       struct strbuf cfg_sb = STRBUF_INIT;
        struct strbuf rel_path = STRBUF_INIT;
-       char *git_dir = real_pathdup(git_dir_, 1);
-       char *work_tree = real_pathdup(work_tree_, 1);
+       char *git_dir, *work_tree;
 
-       /* Update gitfile */
-       strbuf_addf(&file_name, "%s/.git", work_tree);
-       write_file(file_name.buf, "gitdir: %s",
-                  relative_path(git_dir, work_tree, &rel_path));
+       /* Prepare .git file */
+       strbuf_addf(&gitfile_sb, "%s/.git", work_tree_);
+       if (safe_create_leading_directories_const(gitfile_sb.buf))
+               die(_("could not create directories for %s"), gitfile_sb.buf);
+
+       /* Prepare config file */
+       strbuf_addf(&cfg_sb, "%s/config", git_dir_);
+       if (safe_create_leading_directories_const(cfg_sb.buf))
+               die(_("could not create directories for %s"), cfg_sb.buf);
 
+       git_dir = real_pathdup(git_dir_, 1);
+       work_tree = real_pathdup(work_tree_, 1);
+
+       /* Write .git file */
+       write_file(gitfile_sb.buf, "gitdir: %s",
+                  relative_path(git_dir, work_tree, &rel_path));
        /* Update core.worktree setting */
-       strbuf_reset(&file_name);
-       strbuf_addf(&file_name, "%s/config", git_dir);
-       git_config_set_in_file(file_name.buf, "core.worktree",
+       git_config_set_in_file(cfg_sb.buf, "core.worktree",
                               relative_path(work_tree, git_dir, &rel_path));
 
-       strbuf_release(&file_name);
+       strbuf_release(&gitfile_sb);
+       strbuf_release(&cfg_sb);
        strbuf_release(&rel_path);
        free(work_tree);
        free(git_dir);
diff --git a/entry.c b/entry.c
index c6eea240b69eae1a8b19eb61f6bbf0c888c5e1cb..d2b512da90a3cc84d592a2024cc8dac363c9418d 100644 (file)
--- a/entry.c
+++ b/entry.c
@@ -2,6 +2,7 @@
 #include "blob.h"
 #include "dir.h"
 #include "streaming.h"
+#include "submodule.h"
 
 static void create_directories(const char *path, int path_len,
                               const struct checkout *state)
@@ -146,6 +147,7 @@ static int write_entry(struct cache_entry *ce,
        unsigned long size;
        size_t wrote, newsize = 0;
        struct stat st;
+       const struct submodule *sub;
 
        if (ce_mode_s_ifmt == S_IFREG) {
                struct stream_filter *filter = get_stream_filter(ce->name,
@@ -203,6 +205,10 @@ static int write_entry(struct cache_entry *ce,
                        return error("cannot create temporary submodule %s", path);
                if (mkdir(path, 0777) < 0)
                        return error("cannot create submodule directory %s", path);
+               sub = submodule_from_ce(ce);
+               if (sub)
+                       return submodule_move_head(ce->name,
+                               NULL, oid_to_hex(&ce->oid), SUBMODULE_MOVE_HEAD_FORCE);
                break;
        default:
                return error("unknown file mode for %s in index", path);
@@ -259,7 +265,31 @@ int checkout_entry(struct cache_entry *ce,
        strbuf_add(&path, ce->name, ce_namelen(ce));
 
        if (!check_path(path.buf, path.len, &st, state->base_dir_len)) {
+               const struct submodule *sub;
                unsigned changed = ce_match_stat(ce, &st, CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE);
+               /*
+                * Needs to be checked before !changed returns early,
+                * as the possibly empty directory was not changed
+                */
+               sub = submodule_from_ce(ce);
+               if (sub) {
+                       int err;
+                       if (!is_submodule_populated_gently(ce->name, &err)) {
+                               struct stat sb;
+                               if (lstat(ce->name, &sb))
+                                       die(_("could not stat file '%s'"), ce->name);
+                               if (!(st.st_mode & S_IFDIR))
+                                       unlink_or_warn(ce->name);
+
+                               return submodule_move_head(ce->name,
+                                       NULL, oid_to_hex(&ce->oid),
+                                       SUBMODULE_MOVE_HEAD_FORCE);
+                       } else
+                               return submodule_move_head(ce->name,
+                                       "HEAD", oid_to_hex(&ce->oid),
+                                       SUBMODULE_MOVE_HEAD_FORCE);
+               }
+
                if (!changed)
                        return 0;
                if (!state->force) {
index 42dc3106d2fae1976dcfdfbf264a1a9ed96a71b6..ff6e4f06e93d642aa53ba1812c3788b0ccad092e 100644 (file)
@@ -167,8 +167,11 @@ static void setup_git_env(void)
        const char *replace_ref_base;
 
        git_dir = getenv(GIT_DIR_ENVIRONMENT);
-       if (!git_dir)
+       if (!git_dir) {
+               if (!startup_info->have_repository)
+                       die("BUG: setup_git_env called without repository");
                git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
+       }
        gitfile = read_gitfile(git_dir);
        git_dir = xstrdup(gitfile ? gitfile : git_dir);
        if (get_common_dir(&sb, git_dir))
@@ -274,7 +277,7 @@ char *get_object_directory(void)
        return git_object_dir;
 }
 
-int odb_mkstemp(char *template, size_t limit, const char *pattern)
+int odb_mkstemp(struct strbuf *template, const char *pattern)
 {
        int fd;
        /*
@@ -282,32 +285,28 @@ int odb_mkstemp(char *template, size_t limit, const char *pattern)
         * restrictive except to remove write permission.
         */
        int mode = 0444;
-       snprintf(template, limit, "%s/%s",
-                get_object_directory(), pattern);
-       fd = git_mkstemp_mode(template, mode);
+       git_path_buf(template, "objects/%s", pattern);
+       fd = git_mkstemp_mode(template->buf, mode);
        if (0 <= fd)
                return fd;
 
        /* slow path */
        /* some mkstemp implementations erase template on failure */
-       snprintf(template, limit, "%s/%s",
-                get_object_directory(), pattern);
-       safe_create_leading_directories(template);
-       return xmkstemp_mode(template, mode);
+       git_path_buf(template, "objects/%s", pattern);
+       safe_create_leading_directories(template->buf);
+       return xmkstemp_mode(template->buf, mode);
 }
 
-int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1)
+int odb_pack_keep(const char *name)
 {
        int fd;
 
-       snprintf(name, namesz, "%s/pack/pack-%s.keep",
-                get_object_directory(), sha1_to_hex(sha1));
        fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
        if (0 <= fd)
                return fd;
 
        /* slow path */
-       safe_create_leading_directories(name);
+       safe_create_leading_directories_const(name);
        return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 }
 
index 6c13472c42257a47b07058156a7070cf427a038d..1ea3a08609bd8ece7a22ce33c78a0a798304375f 100644 (file)
@@ -890,14 +890,15 @@ static struct tree_content *dup_tree_content(struct tree_content *s)
 
 static void start_packfile(void)
 {
-       static char tmp_file[PATH_MAX];
+       struct strbuf tmp_file = STRBUF_INIT;
        struct packed_git *p;
        struct pack_header hdr;
        int pack_fd;
 
-       pack_fd = odb_mkstemp(tmp_file, sizeof(tmp_file),
-                             "pack/tmp_pack_XXXXXX");
-       FLEX_ALLOC_STR(p, pack_name, tmp_file);
+       pack_fd = odb_mkstemp(&tmp_file, "pack/tmp_pack_XXXXXX");
+       FLEX_ALLOC_STR(p, pack_name, tmp_file.buf);
+       strbuf_release(&tmp_file);
+
        p->pack_fd = pack_fd;
        p->do_not_close = 1;
        pack_file = sha1fd(pack_fd, p->pack_name);
@@ -940,41 +941,40 @@ static const char *create_index(void)
 
 static char *keep_pack(const char *curr_index_name)
 {
-       static char name[PATH_MAX];
        static const char *keep_msg = "fast-import";
+       struct strbuf name = STRBUF_INIT;
        int keep_fd;
 
-       keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
+       odb_pack_name(&name, pack_data->sha1, "keep");
+       keep_fd = odb_pack_keep(name.buf);
        if (keep_fd < 0)
                die_errno("cannot create keep file");
        write_or_die(keep_fd, keep_msg, strlen(keep_msg));
        if (close(keep_fd))
                die_errno("failed to write keep file");
 
-       snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
-                get_object_directory(), sha1_to_hex(pack_data->sha1));
-       if (finalize_object_file(pack_data->pack_name, name))
+       odb_pack_name(&name, pack_data->sha1, "pack");
+       if (finalize_object_file(pack_data->pack_name, name.buf))
                die("cannot store pack file");
 
-       snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
-                get_object_directory(), sha1_to_hex(pack_data->sha1));
-       if (finalize_object_file(curr_index_name, name))
+       odb_pack_name(&name, pack_data->sha1, "idx");
+       if (finalize_object_file(curr_index_name, name.buf))
                die("cannot store index file");
        free((void *)curr_index_name);
-       return name;
+       return strbuf_detach(&name, NULL);
 }
 
 static void unkeep_all_packs(void)
 {
-       static char name[PATH_MAX];
+       struct strbuf name = STRBUF_INIT;
        int k;
 
        for (k = 0; k < pack_id; k++) {
                struct packed_git *p = all_packs[k];
-               snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
-                        get_object_directory(), sha1_to_hex(p->sha1));
-               unlink_or_warn(name);
+               odb_pack_name(&name, p->sha1, "keep");
+               unlink_or_warn(name.buf);
        }
+       strbuf_release(&name);
 }
 
 static int loosen_small_pack(const struct packed_git *p)
@@ -1033,6 +1033,7 @@ static void end_packfile(void)
                        die("core git rejected index %s", idx_name);
                all_packs[pack_id] = new_p;
                install_packed_git(new_p);
+               free(idx_name);
 
                /* Print the boundary */
                if (pack_edges) {
@@ -1173,7 +1174,8 @@ static int store_object(
                delta_count_by_type[type]++;
                e->depth = last->depth + 1;
 
-               hdrlen = encode_in_pack_object_header(OBJ_OFS_DELTA, deltalen, hdr);
+               hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr),
+                                                     OBJ_OFS_DELTA, deltalen);
                sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
 
@@ -1184,7 +1186,8 @@ static int store_object(
                pack_size += sizeof(hdr) - pos;
        } else {
                e->depth = 0;
-               hdrlen = encode_in_pack_object_header(type, dat->len, hdr);
+               hdrlen = encode_in_pack_object_header(hdr, sizeof(hdr),
+                                                     type, dat->len);
                sha1write(pack_file, hdr, hdrlen);
                pack_size += hdrlen;
        }
@@ -1237,9 +1240,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
        sha1file_checkpoint(pack_file, &checkpoint);
        offset = checkpoint.offset;
 
-       hdrlen = snprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
-       if (out_sz <= hdrlen)
-               die("impossibly large object header");
+       hdrlen = xsnprintf((char *)out_buf, out_sz, "blob %" PRIuMAX, len) + 1;
 
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, out_buf, hdrlen);
@@ -1248,9 +1249,7 @@ static void stream_blob(uintmax_t len, unsigned char *sha1out, uintmax_t mark)
 
        git_deflate_init(&s, pack_compression_level);
 
-       hdrlen = encode_in_pack_object_header(OBJ_BLOB, len, out_buf);
-       if (out_sz <= hdrlen)
-               die("impossibly large object header");
+       hdrlen = encode_in_pack_object_header(out_buf, out_sz, OBJ_BLOB, len);
 
        s.next_out = out_buf + hdrlen;
        s.avail_out = out_sz - hdrlen;
@@ -3003,7 +3002,7 @@ static void parse_get_mark(const char *p)
        if (!oe)
                die("Unknown mark: %s", command_buf.buf);
 
-       snprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1));
+       xsnprintf(output, sizeof(output), "%s\n", sha1_to_hex(oe->idx.sha1));
        cat_blob_write(output, 41);
 }
 
index d07d85ce302d39abc8c017c377d4230d729fdeb4..afb8b05024823981be9bedfaa70f7301c6f7076c 100644 (file)
@@ -276,6 +276,8 @@ static enum ack_type get_ack(int fd, unsigned char *result_sha1)
                        return ACK;
                }
        }
+       if (skip_prefix(line, "ERR ", &arg))
+               die(_("remote error: %s"), arg);
        die(_("git fetch-pack: expected ACK/NAK, got '%s'"), line);
 }
 
@@ -802,8 +804,8 @@ static int get_pack(struct fetch_pack_args *args,
                if (args->use_thin_pack)
                        argv_array_push(&cmd.args, "--fix-thin");
                if (args->lock_pack || unpack_limit) {
-                       char hostname[256];
-                       if (gethostname(hostname, sizeof(hostname)))
+                       char hostname[HOST_NAME_MAX + 1];
+                       if (xgethostname(hostname, sizeof(hostname)))
                                xsnprintf(hostname, sizeof(hostname), "localhost");
                        argv_array_pushf(&cmd.args,
                                        "--keep=fetch-pack %"PRIuMAX " on %s",
@@ -1015,7 +1017,7 @@ static void update_shallow(struct fetch_pack_args *args,
                           struct ref **sought, int nr_sought,
                           struct shallow_info *si)
 {
-       struct sha1_array ref = SHA1_ARRAY_INIT;
+       struct oid_array ref = OID_ARRAY_INIT;
        int *status;
        int i;
 
@@ -1038,18 +1040,18 @@ static void update_shallow(struct fetch_pack_args *args,
                 * shallow points that exist in the pack (iow in repo
                 * after get_pack() and reprepare_packed_git())
                 */
-               struct sha1_array extra = SHA1_ARRAY_INIT;
-               unsigned char (*sha1)[20] = si->shallow->sha1;
+               struct oid_array extra = OID_ARRAY_INIT;
+               struct object_id *oid = si->shallow->oid;
                for (i = 0; i < si->shallow->nr; i++)
-                       if (has_sha1_file(sha1[i]))
-                               sha1_array_append(&extra, sha1[i]);
+                       if (has_object_file(&oid[i]))
+                               oid_array_append(&extra, &oid[i]);
                if (extra.nr) {
                        setup_alternate_shallow(&shallow_lock,
                                                &alternate_shallow_file,
                                                &extra);
                        commit_lock_file(&shallow_lock);
                }
-               sha1_array_clear(&extra);
+               oid_array_clear(&extra);
                return;
        }
 
@@ -1060,7 +1062,7 @@ static void update_shallow(struct fetch_pack_args *args,
        if (!si->nr_ours && !si->nr_theirs)
                return;
        for (i = 0; i < nr_sought; i++)
-               sha1_array_append(&ref, sought[i]->old_oid.hash);
+               oid_array_append(&ref, &sought[i]->old_oid);
        si->ref = &ref;
 
        if (args->update_shallow) {
@@ -1070,23 +1072,23 @@ static void update_shallow(struct fetch_pack_args *args,
                 * shallow roots that are actually reachable from new
                 * refs.
                 */
-               struct sha1_array extra = SHA1_ARRAY_INIT;
-               unsigned char (*sha1)[20] = si->shallow->sha1;
+               struct oid_array extra = OID_ARRAY_INIT;
+               struct object_id *oid = si->shallow->oid;
                assign_shallow_commits_to_refs(si, NULL, NULL);
                if (!si->nr_ours && !si->nr_theirs) {
-                       sha1_array_clear(&ref);
+                       oid_array_clear(&ref);
                        return;
                }
                for (i = 0; i < si->nr_ours; i++)
-                       sha1_array_append(&extra, sha1[si->ours[i]]);
+                       oid_array_append(&extra, &oid[si->ours[i]]);
                for (i = 0; i < si->nr_theirs; i++)
-                       sha1_array_append(&extra, sha1[si->theirs[i]]);
+                       oid_array_append(&extra, &oid[si->theirs[i]]);
                setup_alternate_shallow(&shallow_lock,
                                        &alternate_shallow_file,
                                        &extra);
                commit_lock_file(&shallow_lock);
-               sha1_array_clear(&extra);
-               sha1_array_clear(&ref);
+               oid_array_clear(&extra);
+               oid_array_clear(&ref);
                return;
        }
 
@@ -1102,7 +1104,7 @@ static void update_shallow(struct fetch_pack_args *args,
                                sought[i]->status = REF_STATUS_REJECT_SHALLOW;
        }
        free(status);
-       sha1_array_clear(&ref);
+       oid_array_clear(&ref);
 }
 
 struct ref *fetch_pack(struct fetch_pack_args *args,
@@ -1110,7 +1112,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                       const struct ref *ref,
                       const char *dest,
                       struct ref **sought, int nr_sought,
-                      struct sha1_array *shallow,
+                      struct oid_array *shallow,
                       char **pack_lockfile)
 {
        struct ref *ref_cpy;
index a2d46e6e754fef713c42fa34b29588eb14bd0e6a..b6aeb43a8e21437581e0e834e77754922f8c6ee9 100644 (file)
@@ -4,7 +4,7 @@
 #include "string-list.h"
 #include "run-command.h"
 
-struct sha1_array;
+struct oid_array;
 
 struct fetch_pack_args {
        const char *uploadpack;
@@ -42,7 +42,7 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                       const char *dest,
                       struct ref **sought,
                       int nr_sought,
-                      struct sha1_array *shallow,
+                      struct oid_array *shallow,
                       char **pack_lockfile);
 
 /*
diff --git a/fsck.c b/fsck.c
index 939792752bf39c72cc536f00c21e2af8ed854c3e..e6152e4e6d426bd92ae7ae063346f25f85ddd8de 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -132,10 +132,10 @@ static int fsck_msg_type(enum fsck_msg_id msg_id,
 
 static void init_skiplist(struct fsck_options *options, const char *path)
 {
-       static struct sha1_array skiplist = SHA1_ARRAY_INIT;
+       static struct oid_array skiplist = OID_ARRAY_INIT;
        int sorted, fd;
-       char buffer[41];
-       unsigned char sha1[20];
+       char buffer[GIT_MAX_HEXSZ + 1];
+       struct object_id oid;
 
        if (options->skiplist)
                sorted = options->skiplist->sorted;
@@ -148,17 +148,18 @@ static void init_skiplist(struct fsck_options *options, const char *path)
        if (fd < 0)
                die("Could not open skip list: %s", path);
        for (;;) {
+               const char *p;
                int result = read_in_full(fd, buffer, sizeof(buffer));
                if (result < 0)
                        die_errno("Could not read '%s'", path);
                if (!result)
                        break;
-               if (get_sha1_hex(buffer, sha1) || buffer[40] != '\n')
+               if (parse_oid_hex(buffer, &oid, &p) || *p != '\n')
                        die("Invalid SHA-1: %s", buffer);
-               sha1_array_append(&skiplist, sha1);
+               oid_array_append(&skiplist, &oid);
                if (sorted && skiplist.nr > 1 &&
-                               hashcmp(skiplist.sha1[skiplist.nr - 2],
-                                       sha1) > 0)
+                               oidcmp(&skiplist.oid[skiplist.nr - 2],
+                                      &oid) > 0)
                        sorted = 0;
        }
        close(fd);
@@ -279,7 +280,7 @@ static int report(struct fsck_options *options, struct object *object,
                return 0;
 
        if (options->skiplist && object &&
-                       sha1_array_lookup(options->skiplist, object->oid.hash) >= 0)
+                       oid_array_lookup(options->skiplist, &object->oid) >= 0)
                return 0;
 
        if (msg_type == FSCK_FATAL)
diff --git a/fsck.h b/fsck.h
index 1891c1863be85c0607a86023e3da3d39d0dae22c..4525510d99abfc0ae523e0d556345db84fc8c302 100644 (file)
--- a/fsck.h
+++ b/fsck.h
@@ -34,7 +34,7 @@ struct fsck_options {
        fsck_error error_func;
        unsigned strict:1;
        int *msg_type;
-       struct sha1_array *skiplist;
+       struct oid_array *skiplist;
        struct decoration *object_names;
 };
 
index 77b4ed53a8aa76aa03bac8324a297e5c9ca5c340..709a5f6ce6fbdb2da14084e94ae9df1db1c3d0a6 100755 (executable)
@@ -1040,7 +1040,7 @@ sub color_diff {
 marked for applying."),
        checkout_index => N__(
 "If the patch applies cleanly, the edited hunk will immediately be
-marked for discarding"),
+marked for discarding."),
        checkout_head => N__(
 "If the patch applies cleanly, the edited hunk will immediately be
 marked for discarding."),
index e626851fe98673b4ac546864030975870ab4dfa7..bd04564a69a052ce856f2ba120855541c66fc650 100644 (file)
@@ -798,8 +798,6 @@ extern FILE *xfopen(const char *path, const char *mode);
 extern FILE *xfdopen(int fd, const char *mode);
 extern int xmkstemp(char *template);
 extern int xmkstemp_mode(char *template, int mode);
-extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
-extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1);
 extern char *xgetcwd(void);
 extern FILE *fopen_for_writing(const char *path);
 
@@ -886,6 +884,12 @@ static inline size_t xsize_t(off_t len)
 __attribute__((format (printf, 3, 4)))
 extern int xsnprintf(char *dst, size_t max, const char *fmt, ...);
 
+#ifndef HOST_NAME_MAX
+#define HOST_NAME_MAX 256
+#endif
+
+extern int xgethostname(char *buf, size_t len);
+
 /* in ctype.c, for kwset users */
 extern const unsigned char tolower_trans_tbl[256];
 
index eab319d76e4597ebbb27019695fd9ce14d145285..8d151da91b9699e804f4d28b865af7f44138bfc1 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -160,17 +160,42 @@ def p4_write_pipe(c, stdin):
     real_cmd = p4_build_cmd(c)
     return write_pipe(real_cmd, stdin)
 
-def read_pipe(c, ignore_error=False):
+def read_pipe_full(c):
+    """ Read output from  command. Returns a tuple
+        of the return status, stdout text and stderr
+        text.
+    """
     if verbose:
         sys.stderr.write('Reading pipe: %s\n' % str(c))
 
     expand = isinstance(c,basestring)
     p = subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=expand)
     (out, err) = p.communicate()
-    if p.returncode != 0 and not ignore_error:
-        die('Command failed: %s\nError: %s' % (str(c), err))
+    return (p.returncode, out, err)
+
+def read_pipe(c, ignore_error=False):
+    """ Read output from  command. Returns the output text on
+        success. On failure, terminates execution, unless
+        ignore_error is True, when it returns an empty string.
+    """
+    (retcode, out, err) = read_pipe_full(c)
+    if retcode != 0:
+        if ignore_error:
+            out = ""
+        else:
+            die('Command failed: %s\nError: %s' % (str(c), err))
     return out
 
+def read_pipe_text(c):
+    """ Read output from a command with trailing whitespace stripped.
+        On error, returns None.
+    """
+    (retcode, out, err) = read_pipe_full(c)
+    if retcode != 0:
+        return None
+    else:
+        return out.rstrip()
+
 def p4_read_pipe(c, ignore_error=False):
     real_cmd = p4_build_cmd(c)
     return read_pipe(real_cmd, ignore_error)
@@ -577,12 +602,7 @@ def p4Where(depotPath):
     return clientPath
 
 def currentGitBranch():
-    retcode = system(["git", "symbolic-ref", "-q", "HEAD"], ignore_error=True)
-    if retcode != 0:
-        # on a detached head
-        return None
-    else:
-        return read_pipe(["git", "name-rev", "HEAD"]).split(" ")[1].strip()
+    return read_pipe_text(["git", "symbolic-ref", "--short", "-q", "HEAD"])
 
 def isValidGitDir(path):
     return git_dir(path) != None
index 48d7c5ded40e1801a36a897849e0f32b314e5981..db1deed8464f0643763ed6e3c5e54221cad8c985 100755 (executable)
@@ -34,6 +34,7 @@ root!              rebase all reachable commits up to the root(s)
 autosquash         move commits that begin with squash!/fixup! under -i
 committer-date-is-author-date! passed to 'git am'
 ignore-date!       passed to 'git am'
+signoff            passed to 'git am'
 whitespace=!       passed to 'git apply'
 ignore-whitespace! passed to 'git apply'
 C=!                passed to 'git apply'
@@ -321,7 +322,7 @@ do
        --ignore-whitespace)
                git_am_opt="$git_am_opt $1"
                ;;
-       --committer-date-is-author-date|--ignore-date)
+       --committer-date-is-author-date|--ignore-date|--signoff|--no-signoff)
                git_am_opt="$git_am_opt $1"
                force_rebase=t
                ;;
index 9c70662cc81269316375c54cf45a9c838b384a85..2fb651b2b8d9d91a130b1cbd11c3c2b6b1cf961b 100755 (executable)
@@ -299,12 +299,12 @@ push_stash () {
        then
                if test $# != 0
                then
-                       git reset ${GIT_QUIET:+-q} -- "$@"
+                       git reset -q -- "$@"
                        git ls-files -z --modified -- "$@" |
                        git checkout-index -z --force --stdin
-                       git clean --force ${GIT_QUIET:+-q} -d -- "$@"
+                       git clean --force -q -d -- "$@"
                else
-                       git reset --hard ${GIT_QUIET:+-q}
+                       git reset --hard -q
                fi
                test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
                if test -n "$untracked"
@@ -314,7 +314,9 @@ push_stash () {
 
                if test "$keep_index" = "t" && test -n "$i_tree"
                then
-                       git read-tree --reset -u $i_tree
+                       git read-tree --reset $i_tree
+                       git ls-files -z --modified -- "$@" |
+                       git checkout-index -z --force --stdin
                fi
        else
                git apply -R < "$TMP-patch" ||
@@ -322,7 +324,7 @@ push_stash () {
 
                if test "$keep_index" != "t"
                then
-                       git reset
+                       git reset -q -- "$@"
                fi
        fi
 }
index 136e26a2c8d4ca1e835e2873f3fd994864dc135e..c0d0e9a4c63495d4522666b6edf984c01c0cfa23 100755 (executable)
@@ -278,6 +278,20 @@ or you are unsure what this means choose another name with the '--name' option."
        fi &&
        git add --force .gitmodules ||
        die "$(eval_gettext "Failed to register submodule '\$sm_path'")"
+
+       # NEEDSWORK: In a multi-working-tree world, this needs to be
+       # set in the per-worktree config.
+       if git config --get submodule.active >/dev/null
+       then
+               # If the submodule being adding isn't already covered by the
+               # current configured pathspec, set the submodule's active flag
+               if ! git submodule--helper is-active "$sm_path"
+               then
+                       git config submodule."$sm_name".active "true"
+               fi
+       else
+               git config submodule."$sm_name".active "true"
+       fi
 }
 
 #
@@ -318,7 +332,7 @@ cmd_foreach()
                git submodule--helper list --prefix "$wt_prefix" ||
                echo "#unmatched" $?
        } |
-       while read mode sha1 stage sm_path
+       while read -r mode sha1 stage sm_path
        do
                die_if_unmatched "$mode" "$sha1"
                if test -e "$sm_path"/.git
@@ -427,7 +441,7 @@ cmd_deinit()
                git submodule--helper list --prefix "$wt_prefix" "$@" ||
                echo "#unmatched" $?
        } |
-       while read mode sha1 stage sm_path
+       while read -r mode sha1 stage sm_path
        do
                die_if_unmatched "$mode" "$sha1"
                name=$(git submodule--helper name "$sm_path") || exit
@@ -591,7 +605,7 @@ cmd_update()
                "$@" || echo "#unmatched" $?
        } | {
        err=
-       while read mode sha1 stage just_cloned sm_path
+       while read -r mode sha1 stage just_cloned sm_path
        do
                die_if_unmatched "$mode" "$sha1"
 
@@ -833,7 +847,7 @@ cmd_summary() {
        # Get modified modules cared by user
        modules=$(git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- "$@" |
                sane_egrep '^:([0-7]* )?160000' |
-               while read mod_src mod_dst sha1_src sha1_dst status sm_path
+               while read -r mod_src mod_dst sha1_src sha1_dst status sm_path
                do
                        # Always show modules deleted or type-changed (blob<->module)
                        if test "$status" = D || test "$status" = T
@@ -859,7 +873,7 @@ cmd_summary() {
        git $diff_cmd $cached --ignore-submodules=dirty --raw $head -- $modules |
        sane_egrep '^:([0-7]* )?160000' |
        cut -c2- |
-       while read mod_src mod_dst sha1_src sha1_dst status name
+       while read -r mod_src mod_dst sha1_src sha1_dst status name
        do
                if test -z "$cached" &&
                        test $sha1_dst = 0000000000000000000000000000000000000000
@@ -1006,18 +1020,17 @@ cmd_status()
                git submodule--helper list --prefix "$wt_prefix" "$@" ||
                echo "#unmatched" $?
        } |
-       while read mode sha1 stage sm_path
+       while read -r mode sha1 stage sm_path
        do
                die_if_unmatched "$mode" "$sha1"
                name=$(git submodule--helper name "$sm_path") || exit
-               url=$(git config submodule."$name".url)
                displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
                if test "$stage" = U
                then
                        say "U$sha1 $displaypath"
                        continue
                fi
-               if test -z "$url" ||
+               if ! git submodule--helper is-active "$sm_path" ||
                {
                        ! test -d "$sm_path"/.git &&
                        ! test -f "$sm_path"/.git
@@ -1087,9 +1100,16 @@ cmd_sync()
                git submodule--helper list --prefix "$wt_prefix" "$@" ||
                echo "#unmatched" $?
        } |
-       while read mode sha1 stage sm_path
+       while read -r mode sha1 stage sm_path
        do
                die_if_unmatched "$mode" "$sha1"
+
+               # skip inactive submodules
+               if ! git submodule--helper is-active "$sm_path"
+               then
+                       continue
+               fi
+
                name=$(git submodule--helper name "$sm_path")
                url=$(git config -f .gitmodules --get submodule."$name".url)
 
@@ -1112,27 +1132,24 @@ cmd_sync()
                        ;;
                esac
 
-               if git config "submodule.$name.url" >/dev/null 2>/dev/null
+               displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
+               say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
+               git config submodule."$name".url "$super_config_url"
+
+               if test -e "$sm_path"/.git
                then
-                       displaypath=$(git submodule--helper relative-path "$prefix$sm_path" "$wt_prefix")
-                       say "$(eval_gettext "Synchronizing submodule url for '\$displaypath'")"
-                       git config submodule."$name".url "$super_config_url"
+               (
+                       sanitize_submodule_env
+                       cd "$sm_path"
+                       remote=$(get_default_remote)
+                       git config remote."$remote".url "$sub_origin_url"
 
-                       if test -e "$sm_path"/.git
+                       if test -n "$recursive"
                        then
-                       (
-                               sanitize_submodule_env
-                               cd "$sm_path"
-                               remote=$(get_default_remote)
-                               git config remote."$remote".url "$sub_origin_url"
-
-                               if test -n "$recursive"
-                               then
-                                       prefix="$prefix$sm_path/"
-                                       eval cmd_sync
-                               fi
-                       )
+                               prefix="$prefix$sm_path/"
+                               eval cmd_sync
                        fi
+               )
                fi
        done
 }
diff --git a/git.c b/git.c
index 33f52acbcc8647e10e5d675806ca03d33fed899d..8ff44f081d43176474b267de5451f2c2e88089d0 100644 (file)
--- a/git.c
+++ b/git.c
@@ -361,8 +361,6 @@ static int run_builtin(struct cmd_struct *p, int argc, const char **argv)
        if (!help && get_super_prefix()) {
                if (!(p->option & SUPPORT_SUPER_PREFIX))
                        die("%s doesn't support --super-prefix", p->cmd);
-               if (prefix)
-                       die("can't use --super-prefix from a subdirectory");
        }
 
        if (!help && p->option & NEED_WORK_TREE)
diff --git a/grep.c b/grep.c
index 0dbdc1d007893042dc1005478f3023eed4ecfc12..47cee4506703f5dd2b7db84e09dae93adf99fbaf 100644 (file)
--- a/grep.c
+++ b/grep.c
@@ -12,6 +12,11 @@ static int grep_source_is_binary(struct grep_source *gs);
 
 static struct grep_opt grep_defaults;
 
+static void std_output(struct grep_opt *opt, const void *buf, size_t size)
+{
+       fwrite(buf, size, 1, stdout);
+}
+
 /*
  * Initialize the grep_defaults template with hardcoded defaults.
  * We could let the compiler do this, but without C99 initializers
@@ -42,6 +47,7 @@ void init_grep_defaults(void)
        color_set(opt->color_selected, "");
        color_set(opt->color_sep, GIT_COLOR_CYAN);
        opt->color = -1;
+       opt->output = std_output;
 }
 
 static int parse_pattern_type_arg(const char *opt, const char *arg)
@@ -152,6 +158,7 @@ void grep_init(struct grep_opt *opt, const char *prefix)
        opt->pathname = def->pathname;
        opt->regflags = def->regflags;
        opt->relative = def->relative;
+       opt->output = def->output;
 
        color_set(opt->color_context, def->color_context);
        color_set(opt->color_filename, def->color_filename);
@@ -1164,7 +1171,7 @@ static void show_line(struct grep_opt *opt, char *bol, char *eol,
        }
        if (opt->linenum) {
                char buf[32];
-               snprintf(buf, sizeof(buf), "%d", lno);
+               xsnprintf(buf, sizeof(buf), "%d", lno);
                output_color(opt, buf, strlen(buf), opt->color_lineno);
                output_sep(opt, sign);
        }
@@ -1379,11 +1386,6 @@ static int look_ahead(struct grep_opt *opt,
        return 0;
 }
 
-static void std_output(struct grep_opt *opt, const void *buf, size_t size)
-{
-       fwrite(buf, size, 1, stdout);
-}
-
 static int fill_textconv_grep(struct userdiff_driver *driver,
                              struct grep_source *gs)
 {
@@ -1651,7 +1653,7 @@ static int grep_source_1(struct grep_opt *opt, struct grep_source *gs, int colle
                                     opt->color_filename);
                        output_sep(opt, ':');
                }
-               snprintf(buf, sizeof(buf), "%u\n", count);
+               xsnprintf(buf, sizeof(buf), "%u\n", count);
                opt->output(opt, buf, strlen(buf));
                return 1;
        }
diff --git a/hash.h b/hash.h
new file mode 100644 (file)
index 0000000..a11fc92
--- /dev/null
+++ b/hash.h
@@ -0,0 +1,16 @@
+#ifndef HASH_H
+#define HASH_H
+
+#if defined(SHA1_PPC)
+#include "ppc/sha1.h"
+#elif defined(SHA1_APPLE)
+#include <CommonCrypto/CommonDigest.h>
+#elif defined(SHA1_OPENSSL)
+#include <openssl/sha.h>
+#elif defined(SHA1_DC)
+#include "sha1dc/sha1.h"
+#else /* SHA1_BLK */
+#include "block-sha1/sha1.h"
+#endif
+
+#endif
index b10b642229ca0c3e6eb27142f080921dd5c3aece..7d1044eb5de0faf69a151ca16c1a21500d9d8a10 100644 (file)
--- a/hashmap.c
+++ b/hashmap.c
@@ -50,6 +50,23 @@ unsigned int memihash(const void *buf, size_t len)
        return hash;
 }
 
+/*
+ * Incoporate another chunk of data into a memihash
+ * computation.
+ */
+unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len)
+{
+       unsigned int hash = hash_seed;
+       unsigned char *ucbuf = (unsigned char *) buf;
+       while (len--) {
+               unsigned int c = *ucbuf++;
+               if (c >= 'a' && c <= 'z')
+                       c -= 'a' - 'A';
+               hash = (hash * FNV32_PRIME) ^ c;
+       }
+       return hash;
+}
+
 #define HASHMAP_INITIAL_SIZE 64
 /* grow / shrink by 2^2 */
 #define HASHMAP_RESIZE_BITS 2
@@ -87,11 +104,19 @@ static inline unsigned int bucket(const struct hashmap *map,
        return key->hash & (map->tablesize - 1);
 }
 
+int hashmap_bucket(const struct hashmap *map, unsigned int hash)
+{
+       return hash & (map->tablesize - 1);
+}
+
 static void rehash(struct hashmap *map, unsigned int newsize)
 {
        unsigned int i, oldsize = map->tablesize;
        struct hashmap_entry **oldtable = map->table;
 
+       if (map->disallow_rehash)
+               return;
+
        alloc_table(map, newsize);
        for (i = 0; i < oldsize; i++) {
                struct hashmap_entry *e = oldtable[i];
@@ -124,7 +149,9 @@ void hashmap_init(struct hashmap *map, hashmap_cmp_fn equals_function,
                size_t initial_size)
 {
        unsigned int size = HASHMAP_INITIAL_SIZE;
-       map->size = 0;
+
+       memset(map, 0, sizeof(*map));
+
        map->cmpfn = equals_function ? equals_function : always_equal;
 
        /* calculate initial table size and allocate the table */
index ab7958ae333bcc635ba2ac8e40ee8aa6d8814ab4..de6022a3a916605d8e3330b23259110dd3522c7d 100644 (file)
--- a/hashmap.h
+++ b/hashmap.h
@@ -12,6 +12,7 @@ extern unsigned int strhash(const char *buf);
 extern unsigned int strihash(const char *buf);
 extern unsigned int memhash(const void *buf, size_t len);
 extern unsigned int memihash(const void *buf, size_t len);
+extern unsigned int memihash_cont(unsigned int hash_seed, const void *buf, size_t len);
 
 static inline unsigned int sha1hash(const unsigned char *sha1)
 {
@@ -38,6 +39,7 @@ struct hashmap {
        struct hashmap_entry **table;
        hashmap_cmp_fn cmpfn;
        unsigned int size, tablesize, grow_at, shrink_at;
+       unsigned disallow_rehash : 1;
 };
 
 struct hashmap_iter {
@@ -76,6 +78,29 @@ static inline void *hashmap_get_from_hash(const struct hashmap *map,
        return hashmap_get(map, &key, keydata);
 }
 
+int hashmap_bucket(const struct hashmap *map, unsigned int hash);
+
+/*
+ * Disallow/allow rehashing of the hashmap.
+ * This is useful if the caller knows that the hashmap
+ * needs multi-threaded access.  The caller is still
+ * required to guard/lock searches and inserts in a
+ * manner appropriate to their usage.  This simply
+ * prevents the table from being unexpectedly re-mapped.
+ *
+ * If is up to the caller to ensure that the hashmap is
+ * initialized to a reasonable size to prevent poor
+ * performance.
+ *
+ * When value=1, prevent future rehashes on adds and deleted.
+ * When value=0, allow future rehahses.  This DOES NOT force
+ * a rehash now.
+ */
+static inline void hashmap_disallow_rehash(struct hashmap *map, unsigned value)
+{
+       map->disallow_rehash = value;
+}
+
 /* hashmap_iter functions */
 
 extern void hashmap_iter_init(struct hashmap *map, struct hashmap_iter *iter);
diff --git a/hex.c b/hex.c
index eab7b626ee0c36a7cb48d251cfc2ea885461fe28..28b44118cbf2c75b26d017a34ecfdf65dbf4c7eb 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -85,7 +85,7 @@ char *oid_to_hex_r(char *buffer, const struct object_id *oid)
 char *sha1_to_hex(const unsigned char *sha1)
 {
        static int bufno;
-       static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
+       static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
        bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
        return sha1_to_hex_r(hexbuffer[bufno], sha1);
 }
index 704b1c837c9feaa1215e5fd9767c04275a4c1beb..f0e3108f716f8376ba924c4db1ea6f44b8bd60b9 100644 (file)
@@ -1431,11 +1431,9 @@ static void one_remote_ref(const char *refname)
         */
        if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) {
                obj = lookup_unknown_object(ref->old_oid.hash);
-               if (obj) {
-                       fprintf(stderr, "  fetch %s for %s\n",
-                               oid_to_hex(&ref->old_oid), refname);
-                       add_fetch_request(obj);
-               }
+               fprintf(stderr, "  fetch %s for %s\n",
+                       oid_to_hex(&ref->old_oid), refname);
+               add_fetch_request(obj);
        }
 
        ref->next = remote_refs;
diff --git a/http.c b/http.c
index 96d84bbed3f66153cc3d817f2b342dc5d1600574..d2e11ec6f012d3b2a9570f0c322aebf53a13cc0a 100644 (file)
--- a/http.c
+++ b/http.c
@@ -19,7 +19,7 @@ long int git_curl_ipresolve;
 #endif
 int active_requests;
 int http_is_verbose;
-size_t http_post_buffer = 16 * LARGE_PACKET_MAX;
+ssize_t http_post_buffer = 16 * LARGE_PACKET_MAX;
 
 #if LIBCURL_VERSION_NUM >= 0x070a06
 #define LIBCURL_CAN_HANDLE_AUTH_ANY
@@ -331,7 +331,9 @@ static int http_options(const char *var, const char *value, void *cb)
        }
 
        if (!strcmp("http.postbuffer", var)) {
-               http_post_buffer = git_config_int(var, value);
+               http_post_buffer = git_config_ssize_t(var, value);
+               if (http_post_buffer < 0)
+                       warning(_("negative value for http.postbuffer; defaulting to %d"), LARGE_PACKET_MAX);
                if (http_post_buffer < LARGE_PACKET_MAX)
                        http_post_buffer = LARGE_PACKET_MAX;
                return 0;
@@ -836,8 +838,14 @@ static CURL *get_curl_handle(void)
                }
        }
 
-       if (curl_http_proxy) {
-               curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
+       if (curl_http_proxy && curl_http_proxy[0] == '\0') {
+               /*
+                * Handle case with the empty http.proxy value here to keep
+                * common code clean.
+                * NB: empty option disables proxying at all.
+                */
+               curl_easy_setopt(result, CURLOPT_PROXY, "");
+       } else if (curl_http_proxy) {
 #if LIBCURL_VERSION_NUM >= 0x071800
                if (starts_with(curl_http_proxy, "socks5h"))
                        curl_easy_setopt(result,
@@ -861,6 +869,9 @@ static CURL *get_curl_handle(void)
                        strbuf_release(&url);
                }
 
+               if (!proxy_auth.host)
+                       die("Invalid proxy URL '%s'", curl_http_proxy);
+
                curl_easy_setopt(result, CURLOPT_PROXY, proxy_auth.host);
 #if LIBCURL_VERSION_NUM >= 0x071304
                var_override(&curl_no_proxy, getenv("NO_PROXY"));
@@ -1366,9 +1377,9 @@ static int handle_curl_result(struct slot_results *results)
                 * FAILONERROR it is lost, so we can give only the numeric
                 * status code.
                 */
-               snprintf(curl_errorstr, sizeof(curl_errorstr),
-                        "The requested URL returned error: %ld",
-                        results->http_code);
+               xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+                         "The requested URL returned error: %ld",
+                         results->http_code);
        }
 
        if (results->curl_result == CURLE_OK) {
@@ -1410,8 +1421,8 @@ int run_one_slot(struct active_request_slot *slot,
 {
        slot->results = results;
        if (!start_active_slot(slot)) {
-               snprintf(curl_errorstr, sizeof(curl_errorstr),
-                        "failed to start HTTP request");
+               xsnprintf(curl_errorstr, sizeof(curl_errorstr),
+                         "failed to start HTTP request");
                return HTTP_START_FAILED;
        }
 
diff --git a/http.h b/http.h
index 02bccb7b0caf9f2b1be0b98cb16c6fc84b95cc74..f7bd3b26b0da70e44402579e4912a1b20dcef16e 100644 (file)
--- a/http.h
+++ b/http.h
@@ -111,7 +111,7 @@ extern struct curl_slist *http_copy_default_headers(void);
 extern long int git_curl_ipresolve;
 extern int active_requests;
 extern int http_is_verbose;
-extern size_t http_post_buffer;
+extern ssize_t http_post_buffer;
 extern struct credential http_auth;
 
 extern char curl_errorstr[CURL_ERROR_SIZE];
diff --git a/ident.c b/ident.c
index c0364fe3a1630086e0e3752fe3e7e51e2027ea44..bea871c8e02b7173eeba5527d22f5ae7783c011e 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -120,9 +120,9 @@ static int canonical_name(const char *host, struct strbuf *out)
 
 static void add_domainname(struct strbuf *out, int *is_bogus)
 {
-       char buf[1024];
+       char buf[HOST_NAME_MAX + 1];
 
-       if (gethostname(buf, sizeof(buf))) {
+       if (xgethostname(buf, sizeof(buf))) {
                warning_errno("cannot get host name");
                strbuf_addstr(out, "(none)");
                *is_bogus = 1;
index 5c7e27a89459a9a63018cc469262c82891c6f7b7..857591660f485d65dec3390b22dd2083c94d7a44 100644 (file)
@@ -964,7 +964,7 @@ static struct imap_store *imap_open_store(struct imap_server_conf *srvc, char *f
                int gai;
                char portstr[6];
 
-               snprintf(portstr, sizeof(portstr), "%d", srvc->port);
+               xsnprintf(portstr, sizeof(portstr), "%d", srvc->port);
 
                memset(&hints, 0, sizeof(hints));
                hints.ai_socktype = SOCK_STREAM;
index a489d9d0fbcc37a1198247a8daae4b3104ebe284..68037758f2f01be6edfab34b7871af68a17163eb 100644 (file)
@@ -757,8 +757,13 @@ static int handle_commit_msg(struct mailinfo *mi, struct strbuf *line)
        assert(!mi->filter_stage);
 
        if (mi->header_stage) {
-               if (!line->len || (line->len == 1 && line->buf[0] == '\n'))
+               if (!line->len || (line->len == 1 && line->buf[0] == '\n')) {
+                       if (mi->inbody_header_accum.len) {
+                               flush_inbody_header_accum(mi);
+                               mi->header_stage = 0;
+                       }
                        return 0;
+               }
        }
 
        if (mi->use_inbody_headers && mi->header_stage) {
index 6d9f23e932559c58c9ebf4679a6035889f726ed2..39309efb7f9e21eb7a039b6b17cdb0d9f1859820 100644 (file)
@@ -23,15 +23,21 @@ static int dir_entry_cmp(const struct dir_entry *e1,
                        name ? name : e2->name, e1->namelen);
 }
 
-static struct dir_entry *find_dir_entry(struct index_state *istate,
-               const char *name, unsigned int namelen)
+static struct dir_entry *find_dir_entry__hash(struct index_state *istate,
+               const char *name, unsigned int namelen, unsigned int hash)
 {
        struct dir_entry key;
-       hashmap_entry_init(&key, memihash(name, namelen));
+       hashmap_entry_init(&key, hash);
        key.namelen = namelen;
        return hashmap_get(&istate->dir_hash, &key, name);
 }
 
+static struct dir_entry *find_dir_entry(struct index_state *istate,
+               const char *name, unsigned int namelen)
+{
+       return find_dir_entry__hash(istate, name, namelen, memihash(name, namelen));
+}
+
 static struct dir_entry *hash_dir_entry(struct index_state *istate,
                struct cache_entry *ce, int namelen)
 {
@@ -112,20 +118,495 @@ static int cache_entry_cmp(const struct cache_entry *ce1,
        return remove ? !(ce1 == ce2) : 0;
 }
 
-static void lazy_init_name_hash(struct index_state *istate)
+static int lazy_try_threaded = 1;
+static int lazy_nr_dir_threads;
+
+#ifdef NO_PTHREADS
+
+static inline int lookup_lazy_params(struct index_state *istate)
 {
-       int nr;
+       return 0;
+}
+
+static inline void threaded_lazy_init_name_hash(
+       struct index_state *istate)
+{
+}
+
+#else
+
+#include "thread-utils.h"
+
+/*
+ * Set a minimum number of cache_entries that we will handle per
+ * thread and use that to decide how many threads to run (upto
+ * the number on the system).
+ *
+ * For guidance setting the lower per-thread bound, see:
+ *     t/helper/test-lazy-init-name-hash --analyze
+ */
+#define LAZY_THREAD_COST (2000)
+
+/*
+ * We use n mutexes to guard n partitions of the "istate->dir_hash"
+ * hashtable.  Since "find" and "insert" operations will hash to a
+ * particular bucket and modify/search a single chain, we can say
+ * that "all chains mod n" are guarded by the same mutex -- rather
+ * than having a single mutex to guard the entire table.  (This does
+ * require that we disable "rehashing" on the hashtable.)
+ *
+ * So, a larger value here decreases the probability of a collision
+ * and the time that each thread must wait for the mutex.
+ */
+#define LAZY_MAX_MUTEX   (32)
+
+static pthread_mutex_t *lazy_dir_mutex_array;
+
+/*
+ * An array of lazy_entry items is used by the n threads in
+ * the directory parse (first) phase to (lock-free) store the
+ * intermediate results.  These values are then referenced by
+ * the 2 threads in the second phase.
+ */
+struct lazy_entry {
+       struct dir_entry *dir;
+       unsigned int hash_dir;
+       unsigned int hash_name;
+};
+
+/*
+ * Decide if we want to use threads (if available) to load
+ * the hash tables.  We set "lazy_nr_dir_threads" to zero when
+ * it is not worth it.
+ */
+static int lookup_lazy_params(struct index_state *istate)
+{
+       int nr_cpus;
+
+       lazy_nr_dir_threads = 0;
+
+       if (!lazy_try_threaded)
+               return 0;
+
+       /*
+        * If we are respecting case, just use the original
+        * code to build the "istate->name_hash".  We don't
+        * need the complexity here.
+        */
+       if (!ignore_case)
+               return 0;
+
+       nr_cpus = online_cpus();
+       if (nr_cpus < 2)
+               return 0;
+
+       if (istate->cache_nr < 2 * LAZY_THREAD_COST)
+               return 0;
+
+       if (istate->cache_nr < nr_cpus * LAZY_THREAD_COST)
+               nr_cpus = istate->cache_nr / LAZY_THREAD_COST;
+       lazy_nr_dir_threads = nr_cpus;
+       return lazy_nr_dir_threads;
+}
+
+/*
+ * Initialize n mutexes for use when searching and inserting
+ * into "istate->dir_hash".  All "dir" threads are trying
+ * to insert partial pathnames into the hash as they iterate
+ * over their portions of the index, so lock contention is
+ * high.
+ *
+ * However, the hashmap is going to put items into bucket
+ * chains based on their hash values.  Use that to create n
+ * mutexes and lock on mutex[bucket(hash) % n].  This will
+ * decrease the collision rate by (hopefully) by a factor of n.
+ */
+static void init_dir_mutex(void)
+{
+       int j;
+
+       lazy_dir_mutex_array = xcalloc(LAZY_MAX_MUTEX, sizeof(pthread_mutex_t));
+
+       for (j = 0; j < LAZY_MAX_MUTEX; j++)
+               init_recursive_mutex(&lazy_dir_mutex_array[j]);
+}
+
+static void cleanup_dir_mutex(void)
+{
+       int j;
+
+       for (j = 0; j < LAZY_MAX_MUTEX; j++)
+               pthread_mutex_destroy(&lazy_dir_mutex_array[j]);
+
+       free(lazy_dir_mutex_array);
+}
+
+static void lock_dir_mutex(int j)
+{
+       pthread_mutex_lock(&lazy_dir_mutex_array[j]);
+}
+
+static void unlock_dir_mutex(int j)
+{
+       pthread_mutex_unlock(&lazy_dir_mutex_array[j]);
+}
+
+static inline int compute_dir_lock_nr(
+       const struct hashmap *map,
+       unsigned int hash)
+{
+       return hashmap_bucket(map, hash) % LAZY_MAX_MUTEX;
+}
+
+static struct dir_entry *hash_dir_entry_with_parent_and_prefix(
+       struct index_state *istate,
+       struct dir_entry *parent,
+       struct strbuf *prefix)
+{
+       struct dir_entry *dir;
+       unsigned int hash;
+       int lock_nr;
+
+       /*
+        * Either we have a parent directory and path with slash(es)
+        * or the directory is an immediate child of the root directory.
+        */
+       assert((parent != NULL) ^ (strchr(prefix->buf, '/') == NULL));
+
+       if (parent)
+               hash = memihash_cont(parent->ent.hash,
+                       prefix->buf + parent->namelen,
+                       prefix->len - parent->namelen);
+       else
+               hash = memihash(prefix->buf, prefix->len);
+
+       lock_nr = compute_dir_lock_nr(&istate->dir_hash, hash);
+       lock_dir_mutex(lock_nr);
+
+       dir = find_dir_entry__hash(istate, prefix->buf, prefix->len, hash);
+       if (!dir) {
+               FLEX_ALLOC_MEM(dir, name, prefix->buf, prefix->len);
+               hashmap_entry_init(dir, hash);
+               dir->namelen = prefix->len;
+               dir->parent = parent;
+               hashmap_add(&istate->dir_hash, dir);
+
+               if (parent) {
+                       unlock_dir_mutex(lock_nr);
+
+                       /* All I really need here is an InterlockedIncrement(&(parent->nr)) */
+                       lock_nr = compute_dir_lock_nr(&istate->dir_hash, parent->ent.hash);
+                       lock_dir_mutex(lock_nr);
+                       parent->nr++;
+               }
+       }
+
+       unlock_dir_mutex(lock_nr);
 
+       return dir;
+}
+
+/*
+ * handle_range_1() and handle_range_dir() are derived from
+ * clear_ce_flags_1() and clear_ce_flags_dir() in unpack-trees.c
+ * and handle the iteration over the entire array of index entries.
+ * They use recursion for adjacent entries in the same parent
+ * directory.
+ */
+static int handle_range_1(
+       struct index_state *istate,
+       int k_start,
+       int k_end,
+       struct dir_entry *parent,
+       struct strbuf *prefix,
+       struct lazy_entry *lazy_entries);
+
+static int handle_range_dir(
+       struct index_state *istate,
+       int k_start,
+       int k_end,
+       struct dir_entry *parent,
+       struct strbuf *prefix,
+       struct lazy_entry *lazy_entries,
+       struct dir_entry **dir_new_out)
+{
+       int rc, k;
+       int input_prefix_len = prefix->len;
+       struct dir_entry *dir_new;
+
+       dir_new = hash_dir_entry_with_parent_and_prefix(istate, parent, prefix);
+
+       strbuf_addch(prefix, '/');
+
+       /*
+        * Scan forward in the index array for index entries having the same
+        * path prefix (that are also in this directory).
+        */
+       if (k_start + 1 >= k_end)
+               k = k_end;
+       else if (strncmp(istate->cache[k_start + 1]->name, prefix->buf, prefix->len) > 0)
+               k = k_start + 1;
+       else if (strncmp(istate->cache[k_end - 1]->name, prefix->buf, prefix->len) == 0)
+               k = k_end;
+       else {
+               int begin = k_start;
+               int end = k_end;
+               while (begin < end) {
+                       int mid = (begin + end) >> 1;
+                       int cmp = strncmp(istate->cache[mid]->name, prefix->buf, prefix->len);
+                       if (cmp == 0) /* mid has same prefix; look in second part */
+                               begin = mid + 1;
+                       else if (cmp > 0) /* mid is past group; look in first part */
+                               end = mid;
+                       else
+                               die("cache entry out of order");
+               }
+               k = begin;
+       }
+
+       /*
+        * Recurse and process what we can of this subset [k_start, k).
+        */
+       rc = handle_range_1(istate, k_start, k, dir_new, prefix, lazy_entries);
+
+       strbuf_setlen(prefix, input_prefix_len);
+
+       *dir_new_out = dir_new;
+       return rc;
+}
+
+static int handle_range_1(
+       struct index_state *istate,
+       int k_start,
+       int k_end,
+       struct dir_entry *parent,
+       struct strbuf *prefix,
+       struct lazy_entry *lazy_entries)
+{
+       int input_prefix_len = prefix->len;
+       int k = k_start;
+
+       while (k < k_end) {
+               struct cache_entry *ce_k = istate->cache[k];
+               const char *name, *slash;
+
+               if (prefix->len && strncmp(ce_k->name, prefix->buf, prefix->len))
+                       break;
+
+               name = ce_k->name + prefix->len;
+               slash = strchr(name, '/');
+
+               if (slash) {
+                       int len = slash - name;
+                       int processed;
+                       struct dir_entry *dir_new;
+
+                       strbuf_add(prefix, name, len);
+                       processed = handle_range_dir(istate, k, k_end, parent, prefix, lazy_entries, &dir_new);
+                       if (processed) {
+                               k += processed;
+                               strbuf_setlen(prefix, input_prefix_len);
+                               continue;
+                       }
+
+                       strbuf_addch(prefix, '/');
+                       processed = handle_range_1(istate, k, k_end, dir_new, prefix, lazy_entries);
+                       k += processed;
+                       strbuf_setlen(prefix, input_prefix_len);
+                       continue;
+               }
+
+               /*
+                * It is too expensive to take a lock to insert "ce_k"
+                * into "istate->name_hash" and increment the ref-count
+                * on the "parent" dir.  So we defer actually updating
+                * permanent data structures until phase 2 (where we
+                * can change the locking requirements) and simply
+                * accumulate our current results into the lazy_entries
+                * data array).
+                *
+                * We do not need to lock the lazy_entries array because
+                * we have exclusive access to the cells in the range
+                * [k_start,k_end) that this thread was given.
+                */
+               lazy_entries[k].dir = parent;
+               if (parent) {
+                       lazy_entries[k].hash_name = memihash_cont(
+                               parent->ent.hash,
+                               ce_k->name + parent->namelen,
+                               ce_namelen(ce_k) - parent->namelen);
+                       lazy_entries[k].hash_dir = parent->ent.hash;
+               } else {
+                       lazy_entries[k].hash_name = memihash(ce_k->name, ce_namelen(ce_k));
+               }
+
+               k++;
+       }
+
+       return k - k_start;
+}
+
+struct lazy_dir_thread_data {
+       pthread_t pthread;
+       struct index_state *istate;
+       struct lazy_entry *lazy_entries;
+       int k_start;
+       int k_end;
+};
+
+static void *lazy_dir_thread_proc(void *_data)
+{
+       struct lazy_dir_thread_data *d = _data;
+       struct strbuf prefix = STRBUF_INIT;
+       handle_range_1(d->istate, d->k_start, d->k_end, NULL, &prefix, d->lazy_entries);
+       strbuf_release(&prefix);
+       return NULL;
+}
+
+struct lazy_name_thread_data {
+       pthread_t pthread;
+       struct index_state *istate;
+       struct lazy_entry *lazy_entries;
+};
+
+static void *lazy_name_thread_proc(void *_data)
+{
+       struct lazy_name_thread_data *d = _data;
+       int k;
+
+       for (k = 0; k < d->istate->cache_nr; k++) {
+               struct cache_entry *ce_k = d->istate->cache[k];
+               ce_k->ce_flags |= CE_HASHED;
+               hashmap_entry_init(ce_k, d->lazy_entries[k].hash_name);
+               hashmap_add(&d->istate->name_hash, ce_k);
+       }
+
+       return NULL;
+}
+
+static inline void lazy_update_dir_ref_counts(
+       struct index_state *istate,
+       struct lazy_entry *lazy_entries)
+{
+       int k;
+
+       for (k = 0; k < istate->cache_nr; k++) {
+               if (lazy_entries[k].dir)
+                       lazy_entries[k].dir->nr++;
+       }
+}
+
+static void threaded_lazy_init_name_hash(
+       struct index_state *istate)
+{
+       int nr_each;
+       int k_start;
+       int t;
+       struct lazy_entry *lazy_entries;
+       struct lazy_dir_thread_data *td_dir;
+       struct lazy_name_thread_data *td_name;
+
+       k_start = 0;
+       nr_each = DIV_ROUND_UP(istate->cache_nr, lazy_nr_dir_threads);
+
+       lazy_entries = xcalloc(istate->cache_nr, sizeof(struct lazy_entry));
+       td_dir = xcalloc(lazy_nr_dir_threads, sizeof(struct lazy_dir_thread_data));
+       td_name = xcalloc(1, sizeof(struct lazy_name_thread_data));
+
+       init_dir_mutex();
+
+       /*
+        * Phase 1:
+        * Build "istate->dir_hash" using n "dir" threads (and a read-only index).
+        */
+       for (t = 0; t < lazy_nr_dir_threads; t++) {
+               struct lazy_dir_thread_data *td_dir_t = td_dir + t;
+               td_dir_t->istate = istate;
+               td_dir_t->lazy_entries = lazy_entries;
+               td_dir_t->k_start = k_start;
+               k_start += nr_each;
+               if (k_start > istate->cache_nr)
+                       k_start = istate->cache_nr;
+               td_dir_t->k_end = k_start;
+               if (pthread_create(&td_dir_t->pthread, NULL, lazy_dir_thread_proc, td_dir_t))
+                       die("unable to create lazy_dir_thread");
+       }
+       for (t = 0; t < lazy_nr_dir_threads; t++) {
+               struct lazy_dir_thread_data *td_dir_t = td_dir + t;
+               if (pthread_join(td_dir_t->pthread, NULL))
+                       die("unable to join lazy_dir_thread");
+       }
+
+       /*
+        * Phase 2:
+        * Iterate over all index entries and add them to the "istate->name_hash"
+        * using a single "name" background thread.
+        * (Testing showed it wasn't worth running more than 1 thread for this.)
+        *
+        * Meanwhile, finish updating the parent directory ref-counts for each
+        * index entry using the current thread.  (This step is very fast and
+        * doesn't need threading.)
+        */
+       td_name->istate = istate;
+       td_name->lazy_entries = lazy_entries;
+       if (pthread_create(&td_name->pthread, NULL, lazy_name_thread_proc, td_name))
+               die("unable to create lazy_name_thread");
+
+       lazy_update_dir_ref_counts(istate, lazy_entries);
+
+       if (pthread_join(td_name->pthread, NULL))
+               die("unable to join lazy_name_thread");
+
+       cleanup_dir_mutex();
+
+       free(td_name);
+       free(td_dir);
+       free(lazy_entries);
+}
+
+#endif
+
+static void lazy_init_name_hash(struct index_state *istate)
+{
        if (istate->name_hash_initialized)
                return;
        hashmap_init(&istate->name_hash, (hashmap_cmp_fn) cache_entry_cmp,
                        istate->cache_nr);
-       hashmap_init(&istate->dir_hash, (hashmap_cmp_fn) dir_entry_cmp, 0);
-       for (nr = 0; nr < istate->cache_nr; nr++)
-               hash_index_entry(istate, istate->cache[nr]);
+       hashmap_init(&istate->dir_hash, (hashmap_cmp_fn) dir_entry_cmp,
+                       istate->cache_nr);
+
+       if (lookup_lazy_params(istate)) {
+               hashmap_disallow_rehash(&istate->dir_hash, 1);
+               threaded_lazy_init_name_hash(istate);
+               hashmap_disallow_rehash(&istate->dir_hash, 0);
+       } else {
+               int nr;
+               for (nr = 0; nr < istate->cache_nr; nr++)
+                       hash_index_entry(istate, istate->cache[nr]);
+       }
+
        istate->name_hash_initialized = 1;
 }
 
+/*
+ * A test routine for t/helper/ sources.
+ *
+ * Returns the number of threads used or 0 when
+ * the non-threaded code path was used.
+ *
+ * Requesting threading WILL NOT override guards
+ * in lookup_lazy_params().
+ */
+int test_lazy_init_name_hash(struct index_state *istate, int try_threaded)
+{
+       lazy_nr_dir_threads = 0;
+       lazy_try_threaded = try_threaded;
+
+       lazy_init_name_hash(istate);
+
+       return lazy_nr_dir_threads;
+}
+
 void add_name_hash(struct index_state *istate, struct cache_entry *ce)
 {
        if (istate->name_hash_initialized)
diff --git a/notes.c b/notes.c
index 2bab961ac122e4da1f7b4a4c4398ac8c72fdfae5..542563b280ad305da1452d57653728b9005874e6 100644 (file)
--- a/notes.c
+++ b/notes.c
@@ -153,8 +153,8 @@ static struct leaf_node *note_tree_find(struct notes_tree *t,
  * How to consolidate an int_node:
  * If there are > 1 non-NULL entries, give up and return non-zero.
  * Otherwise replace the int_node at the given index in the given parent node
- * with the only entry (or a NULL entry if no entries) from the given tree,
- * and return 0.
+ * with the only NOTE entry (or a NULL entry if no entries) from the given
+ * tree, and return 0.
  */
 static int note_tree_consolidate(struct int_node *tree,
        struct int_node *parent, unsigned char index)
@@ -173,6 +173,8 @@ static int note_tree_consolidate(struct int_node *tree,
                }
        }
 
+       if (p && (GET_PTR_TYPE(p) != PTR_TYPE_NOTE))
+               return -2;
        /* replace tree with p in parent[index] */
        parent->a[index] = p;
        free(tree);
index 9705596014c8fcf534fd0478606d478164382913..e313f4f2bc69f967ec4354e80cd62b329c7bcb21 100644 (file)
@@ -508,18 +508,16 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
                          const char *filename,
                          uint16_t options)
 {
-       static char tmp_file[PATH_MAX];
        static uint16_t default_version = 1;
        static uint16_t flags = BITMAP_OPT_FULL_DAG;
+       struct strbuf tmp_file = STRBUF_INIT;
        struct sha1file *f;
 
        struct bitmap_disk_header header;
 
-       int fd = odb_mkstemp(tmp_file, sizeof(tmp_file), "pack/tmp_bitmap_XXXXXX");
+       int fd = odb_mkstemp(&tmp_file, "pack/tmp_bitmap_XXXXXX");
 
-       if (fd < 0)
-               die_errno("unable to create '%s'", tmp_file);
-       f = sha1fd(fd, tmp_file);
+       f = sha1fd(fd, tmp_file.buf);
 
        memcpy(header.magic, BITMAP_IDX_SIGNATURE, sizeof(BITMAP_IDX_SIGNATURE));
        header.version = htons(default_version);
@@ -539,9 +537,11 @@ void bitmap_writer_finish(struct pack_idx_entry **index,
 
        sha1close(f, NULL, CSUM_FSYNC);
 
-       if (adjust_shared_perm(tmp_file))
+       if (adjust_shared_perm(tmp_file.buf))
                die_errno("unable to make temporary bitmap file readable");
 
-       if (rename(tmp_file, filename))
+       if (rename(tmp_file.buf, filename))
                die_errno("unable to rename temporary bitmap file to '%s'", filename);
+
+       strbuf_release(&tmp_file);
 }
index 88bc7f9f7d03557b040b7246cab7d10c66ec380d..fa97b72559883a38be147fab737f6b7adae5975b 100644 (file)
@@ -71,15 +71,15 @@ const char *write_idx_file(const char *index_name, struct pack_idx_entry **objec
                f = sha1fd_check(index_name);
        } else {
                if (!index_name) {
-                       static char tmp_file[PATH_MAX];
-                       fd = odb_mkstemp(tmp_file, sizeof(tmp_file), "pack/tmp_idx_XXXXXX");
-                       index_name = xstrdup(tmp_file);
+                       struct strbuf tmp_file = STRBUF_INIT;
+                       fd = odb_mkstemp(&tmp_file, "pack/tmp_idx_XXXXXX");
+                       index_name = strbuf_detach(&tmp_file, NULL);
                } else {
                        unlink(index_name);
                        fd = open(index_name, O_CREAT|O_EXCL|O_WRONLY, 0600);
+                       if (fd < 0)
+                               die_errno("unable to create '%s'", index_name);
                }
-               if (fd < 0)
-                       die_errno("unable to create '%s'", index_name);
                f = sha1fd(fd, index_name);
        }
 
@@ -304,7 +304,8 @@ char *index_pack_lockfile(int ip_out)
  *  - each byte afterwards: low seven bits are size continuation,
  *    with the high bit being "size continues"
  */
-int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned char *hdr)
+int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
+                                enum object_type type, uintmax_t size)
 {
        int n = 1;
        unsigned char c;
@@ -315,6 +316,8 @@ int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned
        c = (type << 4) | (size & 15);
        size >>= 4;
        while (size) {
+               if (n == hdr_len)
+                       die("object size is too enormous to format");
                *hdr++ = c | 0x80;
                c = size & 0x7f;
                size >>= 7;
@@ -326,11 +329,11 @@ int encode_in_pack_object_header(enum object_type type, uintmax_t size, unsigned
 
 struct sha1file *create_tmp_packfile(char **pack_tmp_name)
 {
-       char tmpname[PATH_MAX];
+       struct strbuf tmpname = STRBUF_INIT;
        int fd;
 
-       fd = odb_mkstemp(tmpname, sizeof(tmpname), "pack/tmp_pack_XXXXXX");
-       *pack_tmp_name = xstrdup(tmpname);
+       fd = odb_mkstemp(&tmpname, "pack/tmp_pack_XXXXXX");
+       *pack_tmp_name = strbuf_detach(&tmpname, NULL);
        return sha1fd(fd, *pack_tmp_name);
 }
 
diff --git a/pack.h b/pack.h
index 0e77429df5e53a753271843aa8abc16f38708ccc..5c2158746ed60f5c1d4f89dce04312e5992b959d 100644 (file)
--- a/pack.h
+++ b/pack.h
@@ -84,7 +84,14 @@ extern int verify_pack(struct packed_git *, verify_fn fn, struct progress *, uin
 extern off_t write_pack_header(struct sha1file *f, uint32_t);
 extern void fixup_pack_header_footer(int, unsigned char *, const char *, uint32_t, unsigned char *, off_t);
 extern char *index_pack_lockfile(int fd);
-extern int encode_in_pack_object_header(enum object_type, uintmax_t, unsigned char *);
+
+/*
+ * The "hdr" output buffer should be at least this big, which will handle sizes
+ * up to 2^67.
+ */
+#define MAX_PACK_OBJECT_HEADER 10
+extern int encode_in_pack_object_header(unsigned char *hdr, int hdr_len,
+                                       enum object_type, uintmax_t);
 
 #define PH_ERROR_EOF           (-1)
 #define PH_ERROR_PACK_SIGNATURE        (-2)
diff --git a/pager.c b/pager.c
index ae79643363091f4967097bdff6e009ea4c7df5e0..c113d898a4ab184cf5ad5ea04dc66365fdf8a8f8 100644 (file)
--- a/pager.c
+++ b/pager.c
@@ -43,37 +43,6 @@ static int core_pager_config(const char *var, const char *value, void *data)
        return 0;
 }
 
-static void read_early_config(config_fn_t cb, void *data)
-{
-       git_config_with_options(cb, data, NULL, 1);
-
-       /*
-        * Note that this is a really dirty hack that does the wrong thing in
-        * many cases. The crux of the problem is that we cannot run
-        * setup_git_directory() early on in git's setup, so we have no idea if
-        * we are in a repository or not, and therefore are not sure whether
-        * and how to read repository-local config.
-        *
-        * So if we _aren't_ in a repository (or we are but we would reject its
-        * core.repositoryformatversion), we'll read whatever is in .git/config
-        * blindly. Similarly, if we _are_ in a repository, but not at the
-        * root, we'll fail to find .git/config (because it's really
-        * ../.git/config, etc). See t7006 for a complete set of failures.
-        *
-        * However, we have historically provided this hack because it does
-        * work some of the time (namely when you are at the top-level of a
-        * valid repository), and would rarely make things worse (i.e., you do
-        * not generally have a .git/config file sitting around).
-        */
-       if (!startup_info->have_repository) {
-               struct git_config_source repo_config;
-
-               memset(&repo_config, 0, sizeof(repo_config));
-               repo_config.file = ".git/config";
-               git_config_with_options(cb, data, &repo_config, 1);
-       }
-}
-
 const char *git_pager(int stdout_is_tty)
 {
        const char *pager;
@@ -166,9 +135,7 @@ void setup_pager(void)
 
 int pager_in_use(void)
 {
-       const char *env;
-       env = getenv("GIT_PAGER_IN_USE");
-       return env ? git_config_bool("GIT_PAGER_IN_USE", env) : 0;
+       return git_env_bool("GIT_PAGER_IN_USE", 0);
 }
 
 /*
index b7d8f7dcb2c3ae7568542832accadab84c7ae314..7419780a9b644e74107eec31d1c75156374920b8 100644 (file)
@@ -96,17 +96,17 @@ int parse_opt_commits(const struct option *opt, const char *arg, int unset)
 
 int parse_opt_object_name(const struct option *opt, const char *arg, int unset)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (unset) {
-               sha1_array_clear(opt->value);
+               oid_array_clear(opt->value);
                return 0;
        }
        if (!arg)
                return -1;
-       if (get_sha1(arg, sha1))
+       if (get_oid(arg, &oid))
                return error(_("malformed object name '%s'"), arg);
-       sha1_array_append(opt->value, sha1);
+       oid_array_append(opt->value, &oid);
        return 0;
 }
 
index 4fbe924a5de27d04b271a11a2ae040e817483951..a23a1e67f04f8072bea357cbed34fdc1b39ab7ae 100644 (file)
@@ -40,7 +40,7 @@ static void fix_filename(const char *prefix, const char **file)
        if (!file || !*file || !prefix || is_absolute_path(*file)
            || !strcmp("-", *file))
                return;
-       *file = xstrdup(prefix_filename(prefix, strlen(prefix), *file));
+       *file = prefix_filename(prefix, *file);
 }
 
 static int opt_command_mode_error(const struct option *opt,
index dcd8a0926cc9c18fd49d800bc83064259eced279..af711227ae3aac7af07de0322a364ccac03b819d 100644 (file)
@@ -258,7 +258,9 @@ extern int parse_opt_passthru_argv(const struct option *, const char *, int);
          PARSE_OPT_LASTARG_DEFAULT | flag, \
          parse_opt_commits, (intptr_t) "HEAD" \
        }
-#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, 0)
-#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN)
+#define OPT_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("contains", v, h, PARSE_OPT_NONEG)
+#define OPT_NO_CONTAINS(v, h) _OPT_CONTAINS_OR_WITH("no-contains", v, h, PARSE_OPT_NONEG)
+#define OPT_WITH(v, h) _OPT_CONTAINS_OR_WITH("with", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
+#define OPT_WITHOUT(v, h) _OPT_CONTAINS_OR_WITH("without", v, h, PARSE_OPT_HIDDEN | PARSE_OPT_NONEG)
 
 #endif
index ce285c2e0c552ecf725c87052846d755507bd9db..fa8f11de82657c3edf4e5c0869d76ae6f12b57bb 100644 (file)
@@ -71,7 +71,7 @@ static int init_patch_id_entry(struct patch_id *patch,
                               struct commit *commit,
                               struct patch_ids *ids)
 {
-       unsigned char header_only_patch_id[GIT_SHA1_RAWSZ];
+       unsigned char header_only_patch_id[GIT_MAX_RAWSZ];
 
        patch->commit = commit;
        if (commit_patch_id(commit, &ids->diffopts, header_only_patch_id, 1))
index 0f34ea11eadffea14620ea2ad45d69894469aef7..b9e5751f8e2f6ef17bfa444fe6d8f3f2c6b7bb85 100644 (file)
@@ -3,7 +3,7 @@
 
 struct patch_id {
        struct hashmap_entry ent;
-       unsigned char patch_id[GIT_SHA1_RAWSZ];
+       unsigned char patch_id[GIT_MAX_RAWSZ];
        struct commit *commit;
 };
 
diff --git a/path.c b/path.c
index efcedafba6f2c0cae218f2d32425c13272df8dea..c1cb1cf6273f4a5a7ed91d495c55a7d283497460 100644 (file)
--- a/path.c
+++ b/path.c
@@ -471,39 +471,19 @@ const char *worktree_git_path(const struct worktree *wt, const char *fmt, ...)
 }
 
 /* Returns 0 on success, negative on failure. */
-#define SUBMODULE_PATH_ERR_NOT_CONFIGURED -1
 static int do_submodule_path(struct strbuf *buf, const char *path,
                             const char *fmt, va_list args)
 {
-       const char *git_dir;
        struct strbuf git_submodule_common_dir = STRBUF_INIT;
        struct strbuf git_submodule_dir = STRBUF_INIT;
-       const struct submodule *sub;
-       int err = 0;
+       int ret;
 
-       strbuf_addstr(buf, path);
-       strbuf_complete(buf, '/');
-       strbuf_addstr(buf, ".git");
-
-       git_dir = read_gitfile(buf->buf);
-       if (git_dir) {
-               strbuf_reset(buf);
-               strbuf_addstr(buf, git_dir);
-       }
-       if (!is_git_directory(buf->buf)) {
-               gitmodules_config();
-               sub = submodule_from_path(null_sha1, path);
-               if (!sub) {
-                       err = SUBMODULE_PATH_ERR_NOT_CONFIGURED;
-                       goto cleanup;
-               }
-               strbuf_reset(buf);
-               strbuf_git_path(buf, "%s/%s", "modules", sub->name);
-       }
-
-       strbuf_addch(buf, '/');
-       strbuf_addbuf(&git_submodule_dir, buf);
+       ret = submodule_to_gitdir(&git_submodule_dir, path);
+       if (ret)
+               goto cleanup;
 
+       strbuf_complete(&git_submodule_dir, '/');
+       strbuf_addbuf(buf, &git_submodule_dir);
        strbuf_vaddf(buf, fmt, args);
 
        if (get_common_dir_noenv(&git_submodule_common_dir, git_submodule_dir.buf))
@@ -514,8 +494,7 @@ static int do_submodule_path(struct strbuf *buf, const char *path,
 cleanup:
        strbuf_release(&git_submodule_dir);
        strbuf_release(&git_submodule_common_dir);
-
-       return err;
+       return ret;
 }
 
 char *git_pathdup_submodule(const char *path, const char *fmt, ...)
@@ -638,8 +617,10 @@ static struct passwd *getpw_str(const char *username, size_t len)
  * Return a string with ~ and ~user expanded via getpw*.  If buf != NULL,
  * then it is a newly allocated string. Returns NULL on getpw failure or
  * if path is NULL.
+ *
+ * If real_home is true, real_path($HOME) is used in the expansion.
  */
-char *expand_user_path(const char *path)
+char *expand_user_path(const char *path, int real_home)
 {
        struct strbuf user_path = STRBUF_INIT;
        const char *to_copy = path;
@@ -654,7 +635,10 @@ char *expand_user_path(const char *path)
                        const char *home = getenv("HOME");
                        if (!home)
                                goto return_null;
-                       strbuf_addstr(&user_path, home);
+                       if (real_home)
+                               strbuf_addstr(&user_path, real_path(home));
+                       else
+                               strbuf_addstr(&user_path, home);
 #ifdef GIT_WINDOWS_NATIVE
                        convert_slashes(user_path.buf);
 #endif
@@ -723,7 +707,7 @@ const char *enter_repo(const char *path, int strict)
                strbuf_add(&validated_path, path, len);
 
                if (used_path.buf[0] == '~') {
-                       char *newpath = expand_user_path(used_path.buf);
+                       char *newpath = expand_user_path(used_path.buf, 0);
                        if (!newpath)
                                return NULL;
                        strbuf_attach(&used_path, newpath, strlen(newpath),
@@ -1272,6 +1256,21 @@ char *xdg_config_home(const char *filename)
        return NULL;
 }
 
+char *xdg_cache_home(const char *filename)
+{
+       const char *home, *cache_home;
+
+       assert(filename);
+       cache_home = getenv("XDG_CACHE_HOME");
+       if (cache_home && *cache_home)
+               return mkpathdup("%s/git/%s", cache_home, filename);
+
+       home = getenv("HOME");
+       if (home)
+               return mkpathdup("%s/.cache/git/%s", home, filename);
+       return NULL;
+}
+
 GIT_PATH_FUNC(git_path_cherry_pick_head, "CHERRY_PICK_HEAD")
 GIT_PATH_FUNC(git_path_revert_head, "REVERT_HEAD")
 GIT_PATH_FUNC(git_path_squash_msg, "SQUASH_MSG")
index 3079a817a1a3dc5768e8d6a704a2bd3990b3cbcf..50f76fff458e45f57ae67c3474de8378f390f978 100644 (file)
@@ -724,7 +724,7 @@ void clear_pathspec(struct pathspec *pathspec)
                free(pathspec->items[i].match);
                free(pathspec->items[i].original);
 
-               for (j = 0; j < pathspec->items[j].attr_match_nr; j++)
+               for (j = 0; j < pathspec->items[i].attr_match_nr; j++)
                        free(pathspec->items[i].attr_match[j].value);
                free(pathspec->items[i].attr_match);
 
index e9c86f5488307937f2b0205c6937303c0891dcd1..913db393dca5feabe9589627a30dc3a63a808674 100644 (file)
--- a/po/de.po
+++ b/po/de.po
@@ -7530,7 +7530,19 @@ msgstr "git describe [<Optionen>] [<Commit-Angabe>...]"
 msgid "git describe [<options>] --dirty"
 msgstr "git describe [<Optionen>] --dirty"
 
-#: builtin/describe.c:217
+#: builtin/describe.c:52
+msgid "head"
+msgstr "Branch"
+
+#: builtin/describe.c:52
+msgid "lightweight"
+msgstr "nicht-annotiert"
+
+#: builtin/describe.c:52
+msgid "annotated"
+msgstr "annotiert"
+
+#: builtin/describe.c:249
 #, c-format
 msgid "annotated tag %s not available"
 msgstr "annotiertes Tag %s ist nicht verfügbar"
index e44775182392c5672fd6ec84eb954db339e5a1ea..008b335844c48fad9408452c11bd921d7b9b0b44 100644 (file)
@@ -1371,6 +1371,9 @@ struct ondisk_cache_entry_extended {
                            ondisk_cache_entry_extended_size(ce_namelen(ce)) : \
                            ondisk_cache_entry_size(ce_namelen(ce)))
 
+/* Allow fsck to force verification of the index checksum. */
+int verify_index_checksum;
+
 static int verify_hdr(struct cache_header *hdr, unsigned long size)
 {
        git_SHA_CTX c;
@@ -1382,6 +1385,10 @@ static int verify_hdr(struct cache_header *hdr, unsigned long size)
        hdr_version = ntohl(hdr->hdr_version);
        if (hdr_version < INDEX_FORMAT_LB || INDEX_FORMAT_UB < hdr_version)
                return error("bad index version %d", hdr_version);
+
+       if (!verify_index_checksum)
+               return 0;
+
        git_SHA1_Init(&c);
        git_SHA1_Update(&c, hdr, size - 20);
        git_SHA1_Final(sha1, &c);
index 9c82b5b9d632bbbe66332b6c54badb5bc88d28d8..3a640448fd83e6cf0bda8ddda081757d5941361e 100644 (file)
@@ -1487,6 +1487,7 @@ struct ref_filter_cbdata {
        struct ref_array *array;
        struct ref_filter *filter;
        struct contains_cache contains_cache;
+       struct contains_cache no_contains_cache;
 };
 
 /*
@@ -1586,11 +1587,11 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
 }
 
 static int commit_contains(struct ref_filter *filter, struct commit *commit,
-                          struct contains_cache *cache)
+                          struct commit_list *list, struct contains_cache *cache)
 {
        if (filter->with_commit_tag_algo)
-               return contains_tag_algo(commit, filter->with_commit, cache) == CONTAINS_YES;
-       return is_descendant_of(commit, filter->with_commit);
+               return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
+       return is_descendant_of(commit, list);
 }
 
 /*
@@ -1677,22 +1678,22 @@ static int filter_pattern_match(struct ref_filter *filter, const char *refname)
  * the need to parse the object via parse_object(). peel_ref() might be a
  * more efficient alternative to obtain the pointee.
  */
-static const unsigned char *match_points_at(struct sha1_array *points_at,
-                                           const unsigned char *sha1,
-                                           const char *refname)
+static const struct object_id *match_points_at(struct oid_array *points_at,
+                                              const struct object_id *oid,
+                                              const char *refname)
 {
-       const unsigned char *tagged_sha1 = NULL;
+       const struct object_id *tagged_oid = NULL;
        struct object *obj;
 
-       if (sha1_array_lookup(points_at, sha1) >= 0)
-               return sha1;
-       obj = parse_object(sha1);
+       if (oid_array_lookup(points_at, oid) >= 0)
+               return oid;
+       obj = parse_object(oid->hash);
        if (!obj)
                die(_("malformed object at '%s'"), refname);
        if (obj->type == OBJ_TAG)
-               tagged_sha1 = ((struct tag *)obj)->tagged->oid.hash;
-       if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
-               return tagged_sha1;
+               tagged_oid = &((struct tag *)obj)->tagged->oid;
+       if (tagged_oid && oid_array_lookup(points_at, tagged_oid) >= 0)
+               return tagged_oid;
        return NULL;
 }
 
@@ -1772,7 +1773,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
        if (!filter_pattern_match(filter, refname))
                return 0;
 
-       if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))
+       if (filter->points_at.nr && !match_points_at(&filter->points_at, oid, refname))
                return 0;
 
        /*
@@ -1780,13 +1781,17 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
         * obtain the commit using the 'oid' available and discard all
         * non-commits early. The actual filtering is done later.
         */
-       if (filter->merge_commit || filter->with_commit || filter->verbose) {
+       if (filter->merge_commit || filter->with_commit || filter->no_commit || filter->verbose) {
                commit = lookup_commit_reference_gently(oid->hash, 1);
                if (!commit)
                        return 0;
-               /* We perform the filtering for the '--contains' option */
+               /* We perform the filtering for the '--contains' option... */
                if (filter->with_commit &&
-                   !commit_contains(filter, commit, &ref_cbdata->contains_cache))
+                   !commit_contains(filter, commit, filter->with_commit, &ref_cbdata->contains_cache))
+                       return 0;
+               /* ...or for the `--no-contains' option */
+               if (filter->no_commit &&
+                   commit_contains(filter, commit, filter->no_commit, &ref_cbdata->no_contains_cache))
                        return 0;
        }
 
@@ -1887,6 +1892,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
        filter->kind = type & FILTER_REFS_KIND_MASK;
 
        init_contains_cache(&ref_cbdata.contains_cache);
+       init_contains_cache(&ref_cbdata.no_contains_cache);
 
        /*  Simple per-ref filtering */
        if (!filter->kind)
@@ -1911,6 +1917,7 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
        }
 
        clear_contains_cache(&ref_cbdata.contains_cache);
+       clear_contains_cache(&ref_cbdata.no_contains_cache);
 
        /*  Filters that need revision walking */
        if (filter->merge_commit)
@@ -2084,8 +2091,17 @@ int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset)
 {
        struct ref_filter *rf = opt->value;
        unsigned char sha1[20];
+       int no_merged = starts_with(opt->long_name, "no");
+
+       if (rf->merge) {
+               if (no_merged) {
+                       return opterror(opt, "is incompatible with --merged", 0);
+               } else {
+                       return opterror(opt, "is incompatible with --no-merged", 0);
+               }
+       }
 
-       rf->merge = starts_with(opt->long_name, "no")
+       rf->merge = no_merged
                ? REF_FILTER_MERGED_OMIT
                : REF_FILTER_MERGED_INCLUDE;
 
index e738c5dfd364cf6e79ff48d5f154824fb94dc8a8..c20167aa3c785f0060147204d963537f58617570 100644 (file)
@@ -51,8 +51,9 @@ struct ref_array {
 
 struct ref_filter {
        const char **name_patterns;
-       struct sha1_array points_at;
+       struct oid_array points_at;
        struct commit_list *with_commit;
+       struct commit_list *no_commit;
 
        enum {
                REF_FILTER_MERGED_NONE = 0,
diff --git a/refs.c b/refs.c
index e7606716ddfff47b29f90e4d46cb2d59633cb455..a3d5f42e37345096628ab83fee629f22df1bb7b5 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -9,6 +9,7 @@
 #include "refs/refs-internal.h"
 #include "object.h"
 #include "tag.h"
+#include "submodule.h"
 
 /*
  * List of all available backends
@@ -170,11 +171,23 @@ int refname_is_safe(const char *refname)
        return 1;
 }
 
+char *refs_resolve_refdup(struct ref_store *refs,
+                         const char *refname, int resolve_flags,
+                         unsigned char *sha1, int *flags)
+{
+       const char *result;
+
+       result = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
+                                        sha1, flags);
+       return xstrdup_or_null(result);
+}
+
 char *resolve_refdup(const char *refname, int resolve_flags,
                     unsigned char *sha1, int *flags)
 {
-       return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
-                                                 sha1, flags));
+       return refs_resolve_refdup(get_main_ref_store(),
+                                  refname, resolve_flags,
+                                  sha1, flags);
 }
 
 /* The argument to filter_refs */
@@ -184,13 +197,20 @@ struct ref_filter {
        void *cb_data;
 };
 
-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+                      int resolve_flags, unsigned char *sha1, int *flags)
 {
-       if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
+       if (refs_resolve_ref_unsafe(refs, refname, resolve_flags, sha1, flags))
                return 0;
        return -1;
 }
 
+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+{
+       return refs_read_ref_full(get_main_ref_store(), refname,
+                                 resolve_flags, sha1, flags);
+}
+
 int read_ref(const char *refname, unsigned char *sha1)
 {
        return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
@@ -285,34 +305,52 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_li
        for_each_rawref(warn_if_dangling_symref, &data);
 }
 
+int refs_for_each_tag_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_ref_in(refs, "refs/tags/", fn, cb_data);
+}
+
 int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/tags/", fn, cb_data);
+       return refs_for_each_tag_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
+       return refs_for_each_tag_ref(get_submodule_ref_store(submodule),
+                                    fn, cb_data);
+}
+
+int refs_for_each_branch_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_ref_in(refs, "refs/heads/", fn, cb_data);
 }
 
 int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/heads/", fn, cb_data);
+       return refs_for_each_branch_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
+       return refs_for_each_branch_ref(get_submodule_ref_store(submodule),
+                                       fn, cb_data);
+}
+
+int refs_for_each_remote_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_ref_in(refs, "refs/remotes/", fn, cb_data);
 }
 
 int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in("refs/remotes/", fn, cb_data);
+       return refs_for_each_remote_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
+       return refs_for_each_remote_ref(get_submodule_ref_store(submodule),
+                                       fn, cb_data);
 }
 
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@ -366,11 +404,11 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
 
 const char *prettify_refname(const char *name)
 {
-       return name + (
-               starts_with(name, "refs/heads/") ? 11 :
-               starts_with(name, "refs/tags/") ? 10 :
-               starts_with(name, "refs/remotes/") ? 13 :
-               0);
+       if (skip_prefix(name, "refs/heads/", &name) ||
+           skip_prefix(name, "refs/tags/", &name) ||
+           skip_prefix(name, "refs/remotes/", &name))
+               ; /* nothing */
+       return name;
 }
 
 static const char *ref_rev_parse_rules[] = {
@@ -429,29 +467,31 @@ int expand_ref(const char *str, int len, unsigned char *sha1, char **ref)
 {
        const char **p, *r;
        int refs_found = 0;
+       struct strbuf fullref = STRBUF_INIT;
 
        *ref = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
-               char fullref[PATH_MAX];
                unsigned char sha1_from_ref[20];
                unsigned char *this_result;
                int flag;
 
                this_result = refs_found ? sha1_from_ref : sha1;
-               mksnpath(fullref, sizeof(fullref), *p, len, str);
-               r = resolve_ref_unsafe(fullref, RESOLVE_REF_READING,
+               strbuf_reset(&fullref);
+               strbuf_addf(&fullref, *p, len, str);
+               r = resolve_ref_unsafe(fullref.buf, RESOLVE_REF_READING,
                                       this_result, &flag);
                if (r) {
                        if (!refs_found++)
                                *ref = xstrdup(r);
                        if (!warn_ambiguous_refs)
                                break;
-               } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) {
-                       warning("ignoring dangling symref %s.", fullref);
-               } else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) {
-                       warning("ignoring broken ref %s.", fullref);
+               } else if ((flag & REF_ISSYMREF) && strcmp(fullref.buf, "HEAD")) {
+                       warning("ignoring dangling symref %s.", fullref.buf);
+               } else if ((flag & REF_ISBROKEN) && strchr(fullref.buf, '/')) {
+                       warning("ignoring broken ref %s.", fullref.buf);
                }
        }
+       strbuf_release(&fullref);
        return refs_found;
 }
 
@@ -460,21 +500,22 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
        char *last_branch = substitute_branch_name(&str, &len);
        const char **p;
        int logs_found = 0;
+       struct strbuf path = STRBUF_INIT;
 
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
                unsigned char hash[20];
-               char path[PATH_MAX];
                const char *ref, *it;
 
-               mksnpath(path, sizeof(path), *p, len, str);
-               ref = resolve_ref_unsafe(path, RESOLVE_REF_READING,
+               strbuf_reset(&path);
+               strbuf_addf(&path, *p, len, str);
+               ref = resolve_ref_unsafe(path.buf, RESOLVE_REF_READING,
                                         hash, NULL);
                if (!ref)
                        continue;
-               if (reflog_exists(path))
-                       it = path;
-               else if (strcmp(ref, path) && reflog_exists(ref))
+               if (reflog_exists(path.buf))
+                       it = path.buf;
+               else if (strcmp(ref, path.buf) && reflog_exists(ref))
                        it = ref;
                else
                        continue;
@@ -485,6 +526,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
                if (!warn_ambiguous_refs)
                        break;
        }
+       strbuf_release(&path);
        free(last_branch);
        return logs_found;
 }
@@ -592,16 +634,20 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
        return 0;
 }
 
-int delete_ref(const char *msg, const char *refname,
-              const unsigned char *old_sha1, unsigned int flags)
+int refs_delete_ref(struct ref_store *refs, const char *msg,
+                   const char *refname,
+                   const unsigned char *old_sha1,
+                   unsigned int flags)
 {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
-       if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+               assert(refs == get_main_ref_store());
                return delete_pseudoref(refname, old_sha1);
+       }
 
-       transaction = ref_transaction_begin(&err);
+       transaction = ref_store_transaction_begin(refs, &err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname, old_sha1,
                                   flags, msg, &err) ||
@@ -616,6 +662,13 @@ int delete_ref(const char *msg, const char *refname,
        return 0;
 }
 
+int delete_ref(const char *msg, const char *refname,
+              const unsigned char *old_sha1, unsigned int flags)
+{
+       return refs_delete_ref(get_main_ref_store(), msg, refname,
+                              old_sha1, flags);
+}
+
 int copy_reflog_msg(char *buf, const char *msg)
 {
        char *cp = buf;
@@ -775,11 +828,20 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,
        return 1;
 }
 
-struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
+                                                   struct strbuf *err)
 {
+       struct ref_transaction *tr;
        assert(err);
 
-       return xcalloc(1, sizeof(struct ref_transaction));
+       tr = xcalloc(1, sizeof(struct ref_transaction));
+       tr->ref_store = refs;
+       return tr;
+}
+
+struct ref_transaction *ref_transaction_begin(struct strbuf *err)
+{
+       return ref_store_transaction_begin(get_main_ref_store(), err);
 }
 
 void ref_transaction_free(struct ref_transaction *transaction)
@@ -896,18 +958,20 @@ int update_ref_oid(const char *msg, const char *refname,
                old_oid ? old_oid->hash : NULL, flags, onerr);
 }
 
-int update_ref(const char *msg, const char *refname,
-              const unsigned char *new_sha1, const unsigned char *old_sha1,
-              unsigned int flags, enum action_on_err onerr)
+int refs_update_ref(struct ref_store *refs, const char *msg,
+                   const char *refname, const unsigned char *new_sha1,
+                   const unsigned char *old_sha1, unsigned int flags,
+                   enum action_on_err onerr)
 {
        struct ref_transaction *t = NULL;
        struct strbuf err = STRBUF_INIT;
        int ret = 0;
 
        if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+               assert(refs == get_main_ref_store());
                ret = write_pseudoref(refname, new_sha1, old_sha1, &err);
        } else {
-               t = ref_transaction_begin(&err);
+               t = ref_store_transaction_begin(refs, &err);
                if (!t ||
                    ref_transaction_update(t, refname, new_sha1, old_sha1,
                                           flags, msg, &err) ||
@@ -938,12 +1002,22 @@ int update_ref(const char *msg, const char *refname,
        return 0;
 }
 
+int update_ref(const char *msg, const char *refname,
+              const unsigned char *new_sha1,
+              const unsigned char *old_sha1,
+              unsigned int flags, enum action_on_err onerr)
+{
+       return refs_update_ref(get_main_ref_store(), msg, refname, new_sha1,
+                              old_sha1, flags, onerr);
+}
+
 char *shorten_unambiguous_ref(const char *refname, int strict)
 {
        int i;
        static char **scanf_fmts;
        static int nr_rules;
        char *short_name;
+       struct strbuf resolved_buf = STRBUF_INIT;
 
        if (!nr_rules) {
                /*
@@ -1002,7 +1076,6 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                 */
                for (j = 0; j < rules_to_fail; j++) {
                        const char *rule = ref_rev_parse_rules[j];
-                       char refname[PATH_MAX];
 
                        /* skip matched rule */
                        if (i == j)
@@ -1013,9 +1086,10 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                         * (with this previous rule) to a valid ref
                         * read_ref() returns 0 on success
                         */
-                       mksnpath(refname, sizeof(refname),
-                                rule, short_name_len, short_name);
-                       if (ref_exists(refname))
+                       strbuf_reset(&resolved_buf);
+                       strbuf_addf(&resolved_buf, rule,
+                                   short_name_len, short_name);
+                       if (ref_exists(resolved_buf.buf))
                                break;
                }
 
@@ -1023,10 +1097,13 @@ char *shorten_unambiguous_ref(const char *refname, int strict)
                 * short name is non-ambiguous if all previous rules
                 * haven't resolved to a valid ref
                 */
-               if (j == rules_to_fail)
+               if (j == rules_to_fail) {
+                       strbuf_release(&resolved_buf);
                        return short_name;
+               }
        }
 
+       strbuf_release(&resolved_buf);
        free(short_name);
        return xstrdup(refname);
 }
@@ -1119,14 +1196,17 @@ const char *find_descendant_ref(const char *dirname,
        return NULL;
 }
 
-int rename_ref_available(const char *old_refname, const char *new_refname)
+int refs_rename_ref_available(struct ref_store *refs,
+                             const char *old_refname,
+                             const char *new_refname)
 {
        struct string_list skip = STRING_LIST_INIT_NODUP;
        struct strbuf err = STRBUF_INIT;
        int ok;
 
        string_list_insert(&skip, old_refname);
-       ok = !verify_refname_available(new_refname, NULL, &skip, &err);
+       ok = !refs_verify_refname_available(refs, new_refname,
+                                           NULL, &skip, &err);
        if (!ok)
                error("%s", err.buf);
 
@@ -1167,10 +1247,9 @@ int head_ref(each_ref_fn fn, void *cb_data)
  * non-zero value, stop the iteration and return that value;
  * otherwise, return 0.
  */
-static int do_for_each_ref(const char *submodule, const char *prefix,
+static int do_for_each_ref(struct ref_store *refs, const char *prefix,
                           each_ref_fn fn, int trim, int flags, void *cb_data)
 {
-       struct ref_store *refs = get_ref_store(submodule);
        struct ref_iterator *iter;
 
        if (!refs)
@@ -1182,19 +1261,30 @@ static int do_for_each_ref(const char *submodule, const char *prefix,
        return do_for_each_ref_iterator(iter, fn, cb_data);
 }
 
+int refs_for_each_ref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(refs, "", fn, 0, 0, cb_data);
+}
+
 int for_each_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "", fn, 0, 0, cb_data);
+       return refs_for_each_ref(get_main_ref_store(), fn, cb_data);
 }
 
 int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(submodule, "", fn, 0, 0, cb_data);
+       return refs_for_each_ref(get_submodule_ref_store(submodule), fn, cb_data);
+}
+
+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
+                        each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(refs, prefix, fn, strlen(prefix), 0, cb_data);
 }
 
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, prefix, fn, strlen(prefix), 0, cb_data);
+       return refs_for_each_ref_in(get_main_ref_store(), prefix, fn, cb_data);
 }
 
 int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
@@ -1203,19 +1293,23 @@ int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsig
 
        if (broken)
                flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(NULL, prefix, fn, 0, flag, cb_data);
+       return do_for_each_ref(get_main_ref_store(),
+                              prefix, fn, 0, flag, cb_data);
 }
 
 int for_each_ref_in_submodule(const char *submodule, const char *prefix,
-               each_ref_fn fn, void *cb_data)
+                             each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(submodule, prefix, fn, strlen(prefix), 0, cb_data);
+       return refs_for_each_ref_in(get_submodule_ref_store(submodule),
+                                   prefix, fn, cb_data);
 }
 
 int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, git_replace_ref_base, fn,
-                              strlen(git_replace_ref_base), 0, cb_data);
+       return do_for_each_ref(get_main_ref_store(),
+                              git_replace_ref_base, fn,
+                              strlen(git_replace_ref_base),
+                              0, cb_data);
 }
 
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
@@ -1223,19 +1317,25 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data)
        struct strbuf buf = STRBUF_INIT;
        int ret;
        strbuf_addf(&buf, "%srefs/", get_git_namespace());
-       ret = do_for_each_ref(NULL, buf.buf, fn, 0, 0, cb_data);
+       ret = do_for_each_ref(get_main_ref_store(),
+                             buf.buf, fn, 0, 0, cb_data);
        strbuf_release(&buf);
        return ret;
 }
 
-int for_each_rawref(each_ref_fn fn, void *cb_data)
+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       return do_for_each_ref(NULL, "", fn, 0,
+       return do_for_each_ref(refs, "", fn, 0,
                               DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
 }
 
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+       return refs_for_each_rawref(get_main_ref_store(), fn, cb_data);
+}
+
 /* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_recursively(struct ref_store *refs,
+const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
                                    int resolve_flags,
                                    unsigned char *sha1, int *flags)
@@ -1314,7 +1414,7 @@ const char *resolve_ref_recursively(struct ref_store *refs,
 /* backend functions */
 int refs_init_db(struct strbuf *err)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       struct ref_store *refs = get_main_ref_store();
 
        return refs->be->init_db(refs, err);
 }
@@ -1322,7 +1422,7 @@ int refs_init_db(struct strbuf *err)
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
                               unsigned char *sha1, int *flags)
 {
-       return resolve_ref_recursively(get_ref_store(NULL), refname,
+       return refs_resolve_ref_unsafe(get_main_ref_store(), refname,
                                       resolve_flags, sha1, flags);
 }
 
@@ -1343,16 +1443,16 @@ int resolve_gitlink_ref(const char *submodule, const char *refname,
                /* We need to strip off one or more trailing slashes */
                char *stripped = xmemdupz(submodule, len);
 
-               refs = get_ref_store(stripped);
+               refs = get_submodule_ref_store(stripped);
                free(stripped);
        } else {
-               refs = get_ref_store(submodule);
+               refs = get_submodule_ref_store(submodule);
        }
 
        if (!refs)
                return -1;
 
-       if (!resolve_ref_recursively(refs, refname, 0, sha1, &flags) ||
+       if (!refs_resolve_ref_unsafe(refs, refname, 0, sha1, &flags) ||
            is_null_sha1(sha1))
                return -1;
        return 0;
@@ -1395,17 +1495,13 @@ static struct ref_store *main_ref_store;
 static struct hashmap submodule_ref_stores;
 
 /*
- * Return the ref_store instance for the specified submodule (or the
- * main repository if submodule is NULL). If that ref_store hasn't
- * been initialized yet, return NULL.
+ * Return the ref_store instance for the specified submodule. If that
+ * ref_store hasn't been initialized yet, return NULL.
  */
-static struct ref_store *lookup_ref_store(const char *submodule)
+static struct ref_store *lookup_submodule_ref_store(const char *submodule)
 {
        struct submodule_hash_entry *entry;
 
-       if (!submodule)
-               return main_ref_store;
-
        if (!submodule_ref_stores.tablesize)
                /* It's initialized on demand in register_ref_store(). */
                return NULL;
@@ -1415,34 +1511,12 @@ static struct ref_store *lookup_ref_store(const char *submodule)
        return entry ? entry->refs : NULL;
 }
 
-/*
- * Register the specified ref_store to be the one that should be used
- * for submodule (or the main repository if submodule is NULL). It is
- * a fatal error to call this function twice for the same submodule.
- */
-static void register_ref_store(struct ref_store *refs, const char *submodule)
-{
-       if (!submodule) {
-               if (main_ref_store)
-                       die("BUG: main_ref_store initialized twice");
-
-               main_ref_store = refs;
-       } else {
-               if (!submodule_ref_stores.tablesize)
-                       hashmap_init(&submodule_ref_stores, submodule_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);
-       }
-}
-
 /*
  * Create, record, and return a ref_store instance for the specified
- * submodule (or the main repository if submodule is NULL).
+ * gitdir.
  */
-static struct ref_store *ref_store_init(const char *submodule)
+static struct ref_store *ref_store_init(const char *gitdir,
+                                       unsigned int flags)
 {
        const char *be_name = "files";
        struct ref_storage_be *be = find_ref_storage_backend(be_name);
@@ -1451,33 +1525,76 @@ static struct ref_store *ref_store_init(const char *submodule)
        if (!be)
                die("BUG: reference backend %s is unknown", be_name);
 
-       refs = be->init(submodule);
-       register_ref_store(refs, submodule);
+       refs = be->init(gitdir, flags);
        return refs;
 }
 
-struct ref_store *get_ref_store(const char *submodule)
+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));
+       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.
+ */
+static void register_submodule_ref_store(struct ref_store *refs,
+                                        const char *submodule)
+{
+       if (!submodule_ref_stores.tablesize)
+               hashmap_init(&submodule_ref_stores, submodule_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);
+}
+
+struct ref_store *get_submodule_ref_store(const char *submodule)
 {
+       struct strbuf submodule_sb = STRBUF_INIT;
        struct ref_store *refs;
+       int ret;
 
        if (!submodule || !*submodule) {
-               refs = lookup_ref_store(NULL);
+               /*
+                * FIXME: This case is ideally not allowed. But that
+                * can't happen until we clean up all the callers.
+                */
+               return get_main_ref_store();
+       }
 
-               if (!refs)
-                       refs = ref_store_init(NULL);
-       } else {
-               refs = lookup_ref_store(submodule);
+       refs = lookup_submodule_ref_store(submodule);
+       if (refs)
+               return refs;
 
-               if (!refs) {
-                       struct strbuf submodule_sb = STRBUF_INIT;
+       strbuf_addstr(&submodule_sb, submodule);
+       ret = is_nonbare_repository_dir(&submodule_sb);
+       strbuf_release(&submodule_sb);
+       if (!ret)
+               return NULL;
 
-                       strbuf_addstr(&submodule_sb, submodule);
-                       if (is_nonbare_repository_dir(&submodule_sb))
-                               refs = ref_store_init(submodule);
-                       strbuf_release(&submodule_sb);
-               }
+       ret = submodule_to_gitdir(&submodule_sb, submodule);
+       if (ret) {
+               strbuf_release(&submodule_sb);
+               return NULL;
        }
 
+       /* 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);
+
+       strbuf_release(&submodule_sb);
        return refs;
 }
 
@@ -1488,50 +1605,64 @@ void base_ref_store_init(struct ref_store *refs,
 }
 
 /* backend functions */
-int pack_refs(unsigned int flags)
+int refs_pack_refs(struct ref_store *refs, unsigned int flags)
 {
-       struct ref_store *refs = get_ref_store(NULL);
-
        return refs->be->pack_refs(refs, flags);
 }
 
+int refs_peel_ref(struct ref_store *refs, const char *refname,
+                 unsigned char *sha1)
+{
+       return refs->be->peel_ref(refs, refname, sha1);
+}
+
 int peel_ref(const char *refname, unsigned char *sha1)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       return refs_peel_ref(get_main_ref_store(), refname, sha1);
+}
 
-       return refs->be->peel_ref(refs, refname, sha1);
+int refs_create_symref(struct ref_store *refs,
+                      const char *ref_target,
+                      const char *refs_heads_master,
+                      const char *logmsg)
+{
+       return refs->be->create_symref(refs, ref_target,
+                                      refs_heads_master,
+                                      logmsg);
 }
 
 int create_symref(const char *ref_target, const char *refs_heads_master,
                  const char *logmsg)
 {
-       struct ref_store *refs = get_ref_store(NULL);
-
-       return refs->be->create_symref(refs, ref_target, refs_heads_master,
-                                      logmsg);
+       return refs_create_symref(get_main_ref_store(), ref_target,
+                                 refs_heads_master, logmsg);
 }
 
 int ref_transaction_commit(struct ref_transaction *transaction,
                           struct strbuf *err)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       struct ref_store *refs = transaction->ref_store;
+
+       if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
+               strbuf_addstr(err,
+                             _("ref updates forbidden inside quarantine environment"));
+               return -1;
+       }
 
        return refs->be->transaction_commit(refs, transaction, err);
 }
 
-int verify_refname_available(const char *refname,
-                            const struct string_list *extra,
-                            const struct string_list *skip,
-                            struct strbuf *err)
+int refs_verify_refname_available(struct ref_store *refs,
+                                 const char *refname,
+                                 const struct string_list *extra,
+                                 const struct string_list *skip,
+                                 struct strbuf *err)
 {
-       struct ref_store *refs = get_ref_store(NULL);
-
        return refs->be->verify_refname_available(refs, refname, extra, skip, err);
 }
 
-int for_each_reflog(each_ref_fn fn, void *cb_data)
+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
 {
-       struct ref_store *refs = get_ref_store(NULL);
        struct ref_iterator *iter;
 
        iter = refs->be->reflog_iterator_begin(refs);
@@ -1539,43 +1670,84 @@ int for_each_reflog(each_ref_fn fn, void *cb_data)
        return do_for_each_ref_iterator(iter, fn, cb_data);
 }
 
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
-                               void *cb_data)
+int for_each_reflog(each_ref_fn fn, void *cb_data)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       return refs_for_each_reflog(get_main_ref_store(), fn, cb_data);
+}
 
+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
+                                    const char *refname,
+                                    each_reflog_ent_fn fn,
+                                    void *cb_data)
+{
        return refs->be->for_each_reflog_ent_reverse(refs, refname,
                                                     fn, cb_data);
 }
 
+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn,
+                               void *cb_data)
+{
+       return refs_for_each_reflog_ent_reverse(get_main_ref_store(),
+                                               refname, fn, cb_data);
+}
+
+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
+                            each_reflog_ent_fn fn, void *cb_data)
+{
+       return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+}
+
 int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn,
                        void *cb_data)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       return refs_for_each_reflog_ent(get_main_ref_store(), refname,
+                                       fn, cb_data);
+}
 
-       return refs->be->for_each_reflog_ent(refs, refname, fn, cb_data);
+int refs_reflog_exists(struct ref_store *refs, const char *refname)
+{
+       return refs->be->reflog_exists(refs, refname);
 }
 
 int reflog_exists(const char *refname)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       return refs_reflog_exists(get_main_ref_store(), refname);
+}
 
-       return refs->be->reflog_exists(refs, refname);
+int refs_create_reflog(struct ref_store *refs, const char *refname,
+                      int force_create, struct strbuf *err)
+{
+       return refs->be->create_reflog(refs, refname, force_create, err);
 }
 
 int safe_create_reflog(const char *refname, int force_create,
                       struct strbuf *err)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       return refs_create_reflog(get_main_ref_store(), refname,
+                                 force_create, err);
+}
 
-       return refs->be->create_reflog(refs, refname, force_create, err);
+int refs_delete_reflog(struct ref_store *refs, const char *refname)
+{
+       return refs->be->delete_reflog(refs, refname);
 }
 
 int delete_reflog(const char *refname)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       return refs_delete_reflog(get_main_ref_store(), refname);
+}
 
-       return refs->be->delete_reflog(refs, refname);
+int refs_reflog_expire(struct ref_store *refs,
+                      const char *refname, const unsigned char *sha1,
+                      unsigned int flags,
+                      reflog_expiry_prepare_fn prepare_fn,
+                      reflog_expiry_should_prune_fn should_prune_fn,
+                      reflog_expiry_cleanup_fn cleanup_fn,
+                      void *policy_cb_data)
+{
+       return refs->be->reflog_expire(refs, refname, sha1, flags,
+                                      prepare_fn, should_prune_fn,
+                                      cleanup_fn, policy_cb_data);
 }
 
 int reflog_expire(const char *refname, const unsigned char *sha1,
@@ -1585,31 +1757,38 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
                  reflog_expiry_cleanup_fn cleanup_fn,
                  void *policy_cb_data)
 {
-       struct ref_store *refs = get_ref_store(NULL);
-
-       return refs->be->reflog_expire(refs, refname, sha1, flags,
-                                      prepare_fn, should_prune_fn,
-                                      cleanup_fn, policy_cb_data);
+       return refs_reflog_expire(get_main_ref_store(),
+                                 refname, sha1, flags,
+                                 prepare_fn, should_prune_fn,
+                                 cleanup_fn, policy_cb_data);
 }
 
 int initial_ref_transaction_commit(struct ref_transaction *transaction,
                                   struct strbuf *err)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       struct ref_store *refs = transaction->ref_store;
 
        return refs->be->initial_transaction_commit(refs, transaction, err);
 }
 
-int delete_refs(struct string_list *refnames, unsigned int flags)
+int refs_delete_refs(struct ref_store *refs, struct string_list *refnames,
+                    unsigned int flags)
 {
-       struct ref_store *refs = get_ref_store(NULL);
-
        return refs->be->delete_refs(refs, refnames, flags);
 }
 
-int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+int delete_refs(struct string_list *refnames, unsigned int flags)
 {
-       struct ref_store *refs = get_ref_store(NULL);
+       return refs_delete_refs(get_main_ref_store(), refnames, flags);
+}
 
+int refs_rename_ref(struct ref_store *refs, const char *oldref,
+                   const char *newref, const char *logmsg)
+{
        return refs->be->rename_ref(refs, oldref, newref, logmsg);
 }
+
+int rename_ref(const char *oldref, const char *newref, const char *logmsg)
+{
+       return refs_rename_ref(get_main_ref_store(), oldref, newref, logmsg);
+}
diff --git a/refs.h b/refs.h
index 3df0d45ebb6bb5900254edd40383d9de8e539a31..49e97d7d5fdf1ce385ea8917bda00a4cce68561f 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -1,6 +1,11 @@
 #ifndef REFS_H
 #define REFS_H
 
+struct object_id;
+struct ref_store;
+struct strbuf;
+struct string_list;
+
 /*
  * Resolve a reference, recursively following symbolic refererences.
  *
 #define RESOLVE_REF_NO_RECURSE 0x02
 #define RESOLVE_REF_ALLOW_BAD_NAME 0x04
 
+const char *refs_resolve_ref_unsafe(struct ref_store *refs,
+                                   const char *refname,
+                                   int resolve_flags,
+                                   unsigned char *sha1,
+                                   int *flags);
 const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
                               unsigned char *sha1, int *flags);
 
+char *refs_resolve_refdup(struct ref_store *refs,
+                         const char *refname, int resolve_flags,
+                         unsigned char *sha1, int *flags);
 char *resolve_refdup(const char *refname, int resolve_flags,
                     unsigned char *sha1, int *flags);
 
+int refs_read_ref_full(struct ref_store *refs, const char *refname,
+                      int resolve_flags, unsigned char *sha1, int *flags);
 int read_ref_full(const char *refname, int resolve_flags,
                  unsigned char *sha1, int *flags);
 int read_ref(const char *refname, unsigned char *sha1);
 
+/*
+ * Return 0 if a reference named refname could be created without
+ * conflicting with the name of an existing reference. Otherwise,
+ * return a negative value and write an explanation to err. If extras
+ * is non-NULL, it is a list of additional refnames with which refname
+ * is not allowed to conflict. If skip is non-NULL, ignore potential
+ * conflicts with refs in skip (e.g., because they are scheduled for
+ * deletion in the same operation). Behavior is undefined if the same
+ * name is listed in both extras and skip.
+ *
+ * Two reference names conflict if one of them exactly matches the
+ * leading components of the other; e.g., "foo/bar" conflicts with
+ * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
+ * "foo/barbados".
+ *
+ * extras and skip must be sorted.
+ */
+
+int refs_verify_refname_available(struct ref_store *refs,
+                                 const char *refname,
+                                 const struct string_list *extra,
+                                 const struct string_list *skip,
+                                 struct strbuf *err);
+
 int ref_exists(const char *refname);
 
 int should_autocreate_reflog(const char *refname);
@@ -78,6 +117,8 @@ extern int refs_init_db(struct strbuf *err);
  * Symbolic references are considered unpeelable, even if they
  * ultimately resolve to a peelable tag.
  */
+int refs_peel_ref(struct ref_store *refs, const char *refname,
+                 unsigned char *sha1);
 int peel_ref(const char *refname, unsigned char *sha1);
 
 /**
@@ -189,8 +230,19 @@ typedef int each_ref_fn(const char *refname,
  * it is not safe to modify references while an iteration is in
  * progress, unless the same callback function invocation that
  * modifies the reference also returns a nonzero value to immediately
- * stop the iteration.
+ * stop the iteration. Returned references are sorted.
  */
+int refs_for_each_ref(struct ref_store *refs,
+                     each_ref_fn fn, void *cb_data);
+int refs_for_each_ref_in(struct ref_store *refs, const char *prefix,
+                        each_ref_fn fn, void *cb_data);
+int refs_for_each_tag_ref(struct ref_store *refs,
+                         each_ref_fn fn, void *cb_data);
+int refs_for_each_branch_ref(struct ref_store *refs,
+                            each_ref_fn fn, void *cb_data);
+int refs_for_each_remote_ref(struct ref_store *refs,
+                            each_ref_fn fn, void *cb_data);
+
 int head_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref(each_ref_fn fn, void *cb_data);
 int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
@@ -220,6 +272,7 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data);
 int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
 
 /* can be used to learn about broken ref and symref */
+int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
 int for_each_rawref(each_ref_fn fn, void *cb_data);
 
 static inline const char *has_glob_specials(const char *pattern)
@@ -243,7 +296,7 @@ void warn_dangling_symrefs(FILE *fp, const char *msg_fmt,
  * Write a packed-refs file for the current repository.
  * flags: Combination of the above PACK_REFS_* flags.
  */
-int pack_refs(unsigned int flags);
+int refs_pack_refs(struct ref_store *refs, unsigned int flags);
 
 /*
  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.
@@ -258,6 +311,8 @@ int pack_refs(unsigned int flags);
 /*
  * Setup reflog before using. Fill in err and return -1 on failure.
  */
+int refs_create_reflog(struct ref_store *refs, const char *refname,
+                      int force_create, struct strbuf *err);
 int safe_create_reflog(const char *refname, int force_create, struct strbuf *err);
 
 /** Reads log for the value of ref during at_time. **/
@@ -267,6 +322,7 @@ int read_ref_at(const char *refname, unsigned int flags,
                unsigned long *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);
 int reflog_exists(const char *refname);
 
 /*
@@ -276,6 +332,10 @@ int reflog_exists(const char *refname);
  * 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().
  */
+int refs_delete_ref(struct ref_store *refs, const char *msg,
+                   const char *refname,
+                   const unsigned char *old_sha1,
+                   unsigned int flags);
 int delete_ref(const char *msg, const char *refname,
               const unsigned char *old_sha1, unsigned int flags);
 
@@ -285,9 +345,12 @@ int delete_ref(const char *msg, const char *refname,
  * an all-or-nothing transaction). flags is 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);
 
 /** Delete a reflog */
+int refs_delete_reflog(struct ref_store *refs, const char *refname);
 int delete_reflog(const char *refname);
 
 /* iterate over reflog entries */
@@ -296,13 +359,20 @@ typedef int each_reflog_ent_fn(
                const char *committer, unsigned long timestamp,
                int tz, const char *msg, void *cb_data);
 
+int refs_for_each_reflog_ent(struct ref_store *refs, const char *refname,
+                            each_reflog_ent_fn fn, void *cb_data);
+int refs_for_each_reflog_ent_reverse(struct ref_store *refs,
+                                    const char *refname,
+                                    each_reflog_ent_fn fn,
+                                    void *cb_data);
 int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
 int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data);
 
 /*
  * Calls the specified function for each reflog file until it returns nonzero,
- * and returns the value
+ * and returns the value. Reflog file order is unspecified.
  */
+int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data);
 int for_each_reflog(each_ref_fn fn, void *cb_data);
 
 #define REFNAME_ALLOW_ONELEVEL 1
@@ -323,8 +393,12 @@ const char *prettify_refname(const char *refname);
 char *shorten_unambiguous_ref(const char *refname, int strict);
 
 /** rename ref, return 0 on success **/
+int refs_rename_ref(struct ref_store *refs, const char *oldref,
+                   const char *newref, const char *logmsg);
 int rename_ref(const char *oldref, const char *newref, const char *logmsg);
 
+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);
 
 /*
@@ -347,6 +421,8 @@ enum action_on_err {
  * Begin a reference transaction.  The reference transaction must
  * be freed by calling ref_transaction_free().
  */
+struct ref_transaction *ref_store_transaction_begin(struct ref_store *refs,
+                                                   struct strbuf *err);
 struct ref_transaction *ref_transaction_begin(struct strbuf *err);
 
 /*
@@ -481,6 +557,9 @@ void ref_transaction_free(struct ref_transaction *transaction);
  * ref_transaction_update(). Handle errors as requested by the `onerr`
  * argument.
  */
+int refs_update_ref(struct ref_store *refs, const char *msg, const char *refname,
+                   const unsigned char *new_sha1, const unsigned char *old_sha1,
+                   unsigned int flags, enum action_on_err onerr);
 int update_ref(const char *msg, const char *refname,
               const unsigned char *new_sha1, const unsigned char *old_sha1,
               unsigned int flags, enum action_on_err onerr);
@@ -547,6 +626,14 @@ typedef void reflog_expiry_cleanup_fn(void *cb_data);
  * enum expire_reflog_flags. The three function pointers are described
  * above. On success, return zero.
  */
+int refs_reflog_expire(struct ref_store *refs,
+                      const char *refname,
+                      const unsigned char *sha1,
+                      unsigned int flags,
+                      reflog_expiry_prepare_fn prepare_fn,
+                      reflog_expiry_should_prune_fn should_prune_fn,
+                      reflog_expiry_cleanup_fn cleanup_fn,
+                      void *policy_cb_data);
 int reflog_expire(const char *refname, const unsigned char *sha1,
                  unsigned int flags,
                  reflog_expiry_prepare_fn prepare_fn,
@@ -556,4 +643,17 @@ int reflog_expire(const char *refname, const unsigned char *sha1,
 
 int ref_storage_backend_exists(const char *name);
 
+struct ref_store *get_main_ref_store(void);
+/*
+ * Return the ref_store instance for the specified submodule. For the
+ * main repository, use submodule==NULL; such a call cannot fail. For
+ * a submodule, the submodule must exist and be a nonbare repository,
+ * otherwise return NULL. If the requested reference store has not yet
+ * been initialized, initialize it first.
+ *
+ * For backwards compatibility, submodule=="" is treated the same as
+ * submodule==NULL.
+ */
+struct ref_store *get_submodule_ref_store(const char *submodule);
+
 #endif /* REFS_H */
index 50188e92f9fd90ca52d8257f3618678d632eb38a..c9d900fd1287e98ee59421649aee1e34b73e4fa0 100644 (file)
@@ -165,6 +165,10 @@ static struct ref_entry *create_dir_entry(struct files_ref_store *ref_store,
                                          const char *dirname, size_t len,
                                          int incomplete);
 static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
+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,
+                              int flags, struct strbuf *err);
 
 static struct ref_dir *get_ref_dir(struct ref_entry *entry)
 {
@@ -912,13 +916,11 @@ struct packed_ref_cache {
  */
 struct files_ref_store {
        struct ref_store base;
+       unsigned int store_flags;
 
-       /*
-        * The name of the submodule represented by this object, or
-        * NULL if it represents the main repository's reference
-        * store:
-        */
-       const char *submodule;
+       char *gitdir;
+       char *gitcommondir;
+       char *packed_refs_path;
 
        struct ref_entry *loose;
        struct packed_ref_cache *packed;
@@ -975,38 +977,47 @@ static void clear_loose_ref_cache(struct files_ref_store *refs)
  * Create a new submodule ref cache and add it to the internal
  * set of caches.
  */
-static struct ref_store *files_ref_store_create(const char *submodule)
+static struct ref_store *files_ref_store_create(const char *gitdir,
+                                               unsigned int flags)
 {
        struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
        struct ref_store *ref_store = (struct ref_store *)refs;
+       struct strbuf sb = STRBUF_INIT;
 
        base_ref_store_init(ref_store, &refs_be_files);
+       refs->store_flags = flags;
 
-       refs->submodule = xstrdup_or_null(submodule);
+       refs->gitdir = xstrdup(gitdir);
+       get_common_dir_noenv(&sb, gitdir);
+       refs->gitcommondir = strbuf_detach(&sb, NULL);
+       strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
+       refs->packed_refs_path = strbuf_detach(&sb, NULL);
 
        return ref_store;
 }
 
 /*
- * Die if refs is for a submodule (i.e., not for the main repository).
- * caller is used in any necessary error messages.
+ * Die if refs is not the main ref store. caller is used in any
+ * necessary error messages.
  */
 static void files_assert_main_repository(struct files_ref_store *refs,
                                         const char *caller)
 {
-       if (refs->submodule)
-               die("BUG: %s called for a submodule", caller);
+       if (refs->store_flags & REF_STORE_MAIN)
+               return;
+
+       die("BUG: operation %s only allowed for main ref store", caller);
 }
 
 /*
  * Downcast ref_store to files_ref_store. Die if ref_store is not a
- * files_ref_store. If submodule_allowed is not true, then also die if
- * files_ref_store is for a submodule (i.e., not for the main
- * repository). caller is used in any necessary error messages.
+ * files_ref_store. required_flags is compared with ref_store's
+ * store_flags to ensure the ref_store has all required capabilities.
+ * "caller" is used in any necessary error messages.
  */
-static struct files_ref_store *files_downcast(
-               struct ref_store *ref_store, int submodule_allowed,
-               const char *caller)
+static struct files_ref_store *files_downcast(struct ref_store *ref_store,
+                                             unsigned int required_flags,
+                                             const char *caller)
 {
        struct files_ref_store *refs;
 
@@ -1016,8 +1027,9 @@ static struct files_ref_store *files_downcast(
 
        refs = (struct files_ref_store *)ref_store;
 
-       if (!submodule_allowed)
-               files_assert_main_repository(refs, caller);
+       if ((refs->store_flags & required_flags) != required_flags)
+               die("BUG: operation %s requires abilities 0x%x, but only have 0x%x",
+                   caller, required_flags, refs->store_flags);
 
        return refs;
 }
@@ -1150,19 +1162,63 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
        strbuf_release(&line);
 }
 
+static const char *files_packed_refs_path(struct files_ref_store *refs)
+{
+       return refs->packed_refs_path;
+}
+
+static void files_reflog_path(struct files_ref_store *refs,
+                             struct strbuf *sb,
+                             const char *refname)
+{
+       if (!refname) {
+               /*
+                * FIXME: of course this is wrong in multi worktree
+                * setting. To be fixed real soon.
+                */
+               strbuf_addf(sb, "%s/logs", refs->gitcommondir);
+               return;
+       }
+
+       switch (ref_type(refname)) {
+       case REF_TYPE_PER_WORKTREE:
+       case REF_TYPE_PSEUDOREF:
+               strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
+               break;
+       case REF_TYPE_NORMAL:
+               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
+               break;
+       default:
+               die("BUG: unknown ref type %d of ref %s",
+                   ref_type(refname), refname);
+       }
+}
+
+static void files_ref_path(struct files_ref_store *refs,
+                          struct strbuf *sb,
+                          const char *refname)
+{
+       switch (ref_type(refname)) {
+       case REF_TYPE_PER_WORKTREE:
+       case REF_TYPE_PSEUDOREF:
+               strbuf_addf(sb, "%s/%s", refs->gitdir, refname);
+               break;
+       case REF_TYPE_NORMAL:
+               strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
+               break;
+       default:
+               die("BUG: unknown ref type %d of ref %s",
+                   ref_type(refname), refname);
+       }
+}
+
 /*
  * Get the packed_ref_cache for the specified files_ref_store,
  * creating it if necessary.
  */
 static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
 {
-       char *packed_refs_file;
-
-       if (refs->submodule)
-               packed_refs_file = git_pathdup_submodule(refs->submodule,
-                                                        "packed-refs");
-       else
-               packed_refs_file = git_pathdup("packed-refs");
+       const char *packed_refs_file = files_packed_refs_path(refs);
 
        if (refs->packed &&
            !stat_validity_check(&refs->packed->validity, packed_refs_file))
@@ -1181,7 +1237,6 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
                        fclose(f);
                }
        }
-       free(packed_refs_file);
        return refs->packed;
 }
 
@@ -1226,19 +1281,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
        struct strbuf refname;
        struct strbuf path = STRBUF_INIT;
        size_t path_baselen;
-       int err = 0;
 
-       if (refs->submodule)
-               err = strbuf_git_path_submodule(&path, refs->submodule, "%s", dirname);
-       else
-               strbuf_git_path(&path, "%s", dirname);
+       files_ref_path(refs, &path, dirname);
        path_baselen = path.len;
 
-       if (err) {
-               strbuf_release(&path);
-               return;
-       }
-
        d = opendir(path.buf);
        if (!d) {
                strbuf_release(&path);
@@ -1267,7 +1313,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                                         create_dir_entry(refs, refname.buf,
                                                          refname.len, 1));
                } else {
-                       if (!resolve_ref_recursively(&refs->base,
+                       if (!refs_resolve_ref_unsafe(&refs->base,
                                                     refname.buf,
                                                     RESOLVE_REF_READING,
                                                     sha1, &flag)) {
@@ -1359,7 +1405,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
                              struct strbuf *referent, unsigned int *type)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 1, "read_raw_ref");
+               files_downcast(ref_store, REF_STORE_READ, "read_raw_ref");
        struct strbuf sb_contents = STRBUF_INIT;
        struct strbuf sb_path = STRBUF_INIT;
        const char *path;
@@ -1373,10 +1419,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
        *type = 0;
        strbuf_reset(&sb_path);
 
-       if (refs->submodule)
-               strbuf_git_path_submodule(&sb_path, refs->submodule, "%s", refname);
-       else
-               strbuf_git_path(&sb_path, "%s", refname);
+       files_ref_path(refs, &sb_path, refname);
 
        path = sb_path.buf;
 
@@ -1564,7 +1607,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
        *lock_p = lock = xcalloc(1, sizeof(*lock));
 
        lock->ref_name = xstrdup(refname);
-       strbuf_git_path(&ref_file, "%s", refname);
+       files_ref_path(refs, &ref_file, refname);
 
 retry:
        switch (safe_create_leading_directories(ref_file.buf)) {
@@ -1579,7 +1622,8 @@ static int lock_raw_ref(struct files_ref_store *refs,
                 * another reference such as "refs/foo". There is no
                 * reason to expect this error to be transitory.
                 */
-               if (verify_refname_available(refname, extras, skip, err)) {
+               if (refs_verify_refname_available(&refs->base, refname,
+                                                 extras, skip, err)) {
                        if (mustexist) {
                                /*
                                 * To the user the relevant error is
@@ -1779,7 +1823,9 @@ static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
 static int files_peel_ref(struct ref_store *ref_store,
                          const char *refname, unsigned char *sha1)
 {
-       struct files_ref_store *refs = files_downcast(ref_store, 0, "peel_ref");
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,
+                              "peel_ref");
        int flag;
        unsigned char base[20];
 
@@ -1792,7 +1838,8 @@ static int files_peel_ref(struct ref_store *ref_store,
                return 0;
        }
 
-       if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
+       if (refs_read_ref_full(ref_store, refname,
+                              RESOLVE_REF_READING, base, &flag))
                return -1;
 
        /*
@@ -1887,21 +1934,21 @@ static struct ref_iterator *files_ref_iterator_begin(
                struct ref_store *ref_store,
                const char *prefix, unsigned int flags)
 {
-       struct files_ref_store *refs =
-               files_downcast(ref_store, 1, "ref_iterator_begin");
+       struct files_ref_store *refs;
        struct ref_dir *loose_dir, *packed_dir;
        struct ref_iterator *loose_iter, *packed_iter;
        struct files_ref_iterator *iter;
        struct ref_iterator *ref_iterator;
 
-       if (!refs)
-               return empty_ref_iterator_begin();
-
        if (ref_paranoia < 0)
                ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
        if (ref_paranoia)
                flags |= DO_FOR_EACH_INCLUDE_BROKEN;
 
+       refs = files_downcast(ref_store,
+                             REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),
+                             "ref_iterator_begin");
+
        iter = xcalloc(1, sizeof(*iter));
        ref_iterator = &iter->base;
        base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);
@@ -1960,15 +2007,15 @@ static struct ref_iterator *files_ref_iterator_begin(
  * on success. On error, write an error message to err, set errno, and
  * return a negative value.
  */
-static int verify_lock(struct ref_lock *lock,
+static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,
                       const unsigned char *old_sha1, int mustexist,
                       struct strbuf *err)
 {
        assert(err);
 
-       if (read_ref_full(lock->ref_name,
-                         mustexist ? RESOLVE_REF_READING : 0,
-                         lock->old_oid.hash, NULL)) {
+       if (refs_read_ref_full(ref_store, lock->ref_name,
+                              mustexist ? RESOLVE_REF_READING : 0,
+                              lock->old_oid.hash, NULL)) {
                if (old_sha1) {
                        int save_errno = errno;
                        strbuf_addf(err, "can't verify ref '%s'", lock->ref_name);
@@ -2036,9 +2083,10 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
        if (flags & REF_DELETING)
                resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
 
-       strbuf_git_path(&ref_file, "%s", refname);
-       resolved = !!resolve_ref_unsafe(refname, resolve_flags,
-                                       lock->old_oid.hash, type);
+       files_ref_path(refs, &ref_file, refname);
+       resolved = !!refs_resolve_ref_unsafe(&refs->base,
+                                            refname, resolve_flags,
+                                            lock->old_oid.hash, type);
        if (!resolved && errno == EISDIR) {
                /*
                 * we are trying to lock foo but we used to
@@ -2055,8 +2103,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                                            refname);
                        goto error_return;
                }
-               resolved = !!resolve_ref_unsafe(refname, resolve_flags,
-                                               lock->old_oid.hash, type);
+               resolved = !!refs_resolve_ref_unsafe(&refs->base,
+                                                    refname, resolve_flags,
+                                                    lock->old_oid.hash, type);
        }
        if (!resolved) {
                last_errno = errno;
@@ -2094,7 +2143,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                goto error_return;
        }
 
-       if (verify_lock(lock, old_sha1, mustexist, err)) {
+       if (verify_lock(&refs->base, lock, old_sha1, mustexist, err)) {
                last_errno = errno;
                goto error_return;
        }
@@ -2157,7 +2206,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)
        }
 
        if (hold_lock_file_for_update_timeout(
-                           &packlock, git_path("packed-refs"),
+                           &packlock, files_packed_refs_path(refs),
                            flags, timeout_value) < 0)
                return -1;
        /*
@@ -2306,9 +2355,12 @@ enum {
  * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or
  * REMOVE_EMPTY_PARENTS_REFLOG.
  */
-static void try_remove_empty_parents(const char *refname, unsigned int flags)
+static void try_remove_empty_parents(struct files_ref_store *refs,
+                                    const char *refname,
+                                    unsigned int flags)
 {
        struct strbuf buf = STRBUF_INIT;
+       struct strbuf sb = STRBUF_INIT;
        char *p, *q;
        int i;
 
@@ -2330,18 +2382,23 @@ static void try_remove_empty_parents(const char *refname, unsigned int flags)
                if (q == p)
                        break;
                strbuf_setlen(&buf, q - buf.buf);
-               if ((flags & REMOVE_EMPTY_PARENTS_REF) &&
-                   rmdir(git_path("%s", buf.buf)))
+
+               strbuf_reset(&sb);
+               files_ref_path(refs, &sb, buf.buf);
+               if ((flags & REMOVE_EMPTY_PARENTS_REF) && rmdir(sb.buf))
                        flags &= ~REMOVE_EMPTY_PARENTS_REF;
-               if ((flags & REMOVE_EMPTY_PARENTS_REFLOG) &&
-                   rmdir(git_path("logs/%s", buf.buf)))
+
+               strbuf_reset(&sb);
+               files_reflog_path(refs, &sb, buf.buf);
+               if ((flags & REMOVE_EMPTY_PARENTS_REFLOG) && rmdir(sb.buf))
                        flags &= ~REMOVE_EMPTY_PARENTS_REFLOG;
        }
        strbuf_release(&buf);
+       strbuf_release(&sb);
 }
 
 /* make sure nobody touched the ref, and unlink */
-static void prune_ref(struct ref_to_prune *r)
+static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
 {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
@@ -2349,7 +2406,7 @@ static void prune_ref(struct ref_to_prune *r)
        if (check_refname_format(r->name, 0))
                return;
 
-       transaction = ref_transaction_begin(&err);
+       transaction = ref_store_transaction_begin(&refs->base, &err);
        if (!transaction ||
            ref_transaction_delete(transaction, r->name, r->sha1,
                                   REF_ISPRUNING | REF_NODEREF, NULL, &err) ||
@@ -2363,10 +2420,10 @@ static void prune_ref(struct ref_to_prune *r)
        strbuf_release(&err);
 }
 
-static void prune_refs(struct ref_to_prune *r)
+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)
 {
        while (r) {
-               prune_ref(r);
+               prune_ref(refs, r);
                r = r->next;
        }
 }
@@ -2374,7 +2431,8 @@ static void prune_refs(struct ref_to_prune *r)
 static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 0, "pack_refs");
+               files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
+                              "pack_refs");
        struct pack_refs_cb_data cbdata;
 
        memset(&cbdata, 0, sizeof(cbdata));
@@ -2389,7 +2447,7 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
        if (commit_packed_refs(refs))
                die_errno("unable to overwrite old ref-pack file");
 
-       prune_refs(cbdata.ref_to_prune);
+       prune_refs(refs, cbdata.ref_to_prune);
        return 0;
 }
 
@@ -2423,7 +2481,7 @@ static int repack_without_refs(struct files_ref_store *refs,
                return 0; /* no refname exists in packed refs */
 
        if (lock_packed_refs(refs, 0)) {
-               unable_to_lock_message(git_path("packed-refs"), errno, err);
+               unable_to_lock_message(files_packed_refs_path(refs), errno, err);
                return -1;
        }
        packed = get_packed_refs(refs);
@@ -2453,7 +2511,7 @@ static int files_delete_refs(struct ref_store *ref_store,
                             struct string_list *refnames, unsigned int flags)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 0, "delete_refs");
+               files_downcast(ref_store, REF_STORE_WRITE, "delete_refs");
        struct strbuf err = STRBUF_INIT;
        int i, result = 0;
 
@@ -2481,7 +2539,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 (delete_ref(NULL, refname, NULL, flags))
+               if (refs_delete_ref(&refs->base, NULL, refname, NULL, flags))
                        result |= error(_("could not remove reference %s"), refname);
        }
 
@@ -2497,13 +2555,18 @@ static int files_delete_refs(struct ref_store *ref_store,
  * IOW, to avoid cross device rename errors, the temporary renamed log must
  * live into logs/refs.
  */
-#define TMP_RENAMED_LOG  "logs/refs/.tmp-renamed-log"
+#define TMP_RENAMED_LOG  "refs/.tmp-renamed-log"
 
-static int rename_tmp_log_callback(const char *path, void *cb)
+struct rename_cb {
+       const char *tmp_renamed_log;
+       int true_errno;
+};
+
+static int rename_tmp_log_callback(const char *path, void *cb_data)
 {
-       int *true_errno = cb;
+       struct rename_cb *cb = cb_data;
 
-       if (rename(git_path(TMP_RENAMED_LOG), path)) {
+       if (rename(cb->tmp_renamed_log, path)) {
                /*
                 * rename(a, b) when b is an existing directory ought
                 * to result in ISDIR, but Solaris 5.8 gives ENOTDIR.
@@ -2511,7 +2574,7 @@ static int rename_tmp_log_callback(const char *path, void *cb)
                 * but report EISDIR to raceproof_create_file() so
                 * that it knows to retry.
                 */
-               *true_errno = errno;
+               cb->true_errno = errno;
                if (errno == ENOTDIR)
                        errno = EISDIR;
                return -1;
@@ -2520,22 +2583,28 @@ static int rename_tmp_log_callback(const char *path, void *cb)
        }
 }
 
-static int rename_tmp_log(const char *newrefname)
+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
 {
-       char *path = git_pathdup("logs/%s", newrefname);
-       int ret, true_errno;
+       struct strbuf path = STRBUF_INIT;
+       struct strbuf tmp = STRBUF_INIT;
+       struct rename_cb cb;
+       int ret;
 
-       ret = raceproof_create_file(path, rename_tmp_log_callback, &true_errno);
+       files_reflog_path(refs, &path, newrefname);
+       files_reflog_path(refs, &tmp, TMP_RENAMED_LOG);
+       cb.tmp_renamed_log = tmp.buf;
+       ret = raceproof_create_file(path.buf, rename_tmp_log_callback, &cb);
        if (ret) {
                if (errno == EISDIR)
-                       error("directory not empty: %s", path);
+                       error("directory not empty: %s", path.buf);
                else
                        error("unable to move logfile %s to %s: %s",
-                             git_path(TMP_RENAMED_LOG), path,
-                             strerror(true_errno));
+                             tmp.buf, path.buf,
+                             strerror(cb.true_errno));
        }
 
-       free(path);
+       strbuf_release(&path);
+       strbuf_release(&tmp);
        return ret;
 }
 
@@ -2546,7 +2615,7 @@ static int files_verify_refname_available(struct ref_store *ref_store,
                                          struct strbuf *err)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 1, "verify_refname_available");
+               files_downcast(ref_store, REF_STORE_READ, "verify_refname_available");
        struct ref_dir *packed_refs = get_packed_refs(refs);
        struct ref_dir *loose_refs = get_loose_refs(refs);
 
@@ -2571,32 +2640,52 @@ static int files_rename_ref(struct ref_store *ref_store,
                            const char *logmsg)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 0, "rename_ref");
+               files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
        unsigned char sha1[20], orig_sha1[20];
        int flag = 0, logmoved = 0;
        struct ref_lock *lock;
        struct stat loginfo;
-       int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
+       struct strbuf sb_oldref = STRBUF_INIT;
+       struct strbuf sb_newref = STRBUF_INIT;
+       struct strbuf tmp_renamed_log = STRBUF_INIT;
+       int log, ret;
        struct strbuf err = STRBUF_INIT;
 
-       if (log && S_ISLNK(loginfo.st_mode))
-               return error("reflog for %s is a symlink", oldrefname);
+       files_reflog_path(refs, &sb_oldref, oldrefname);
+       files_reflog_path(refs, &sb_newref, newrefname);
+       files_reflog_path(refs, &tmp_renamed_log, TMP_RENAMED_LOG);
 
-       if (!resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-                               orig_sha1, &flag))
-               return error("refname %s not found", oldrefname);
+       log = !lstat(sb_oldref.buf, &loginfo);
+       if (log && S_ISLNK(loginfo.st_mode)) {
+               ret = error("reflog for %s is a symlink", oldrefname);
+               goto out;
+       }
 
-       if (flag & REF_ISSYMREF)
-               return error("refname %s is a symbolic ref, renaming it is not supported",
-                       oldrefname);
-       if (!rename_ref_available(oldrefname, newrefname))
-               return 1;
+       if (!refs_resolve_ref_unsafe(&refs->base, oldrefname,
+                                    RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                               orig_sha1, &flag)) {
+               ret = error("refname %s not found", oldrefname);
+               goto out;
+       }
 
-       if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
-               return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
-                       oldrefname, strerror(errno));
+       if (flag & REF_ISSYMREF) {
+               ret = error("refname %s is a symbolic ref, renaming it is not supported",
+                           oldrefname);
+               goto out;
+       }
+       if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {
+               ret = 1;
+               goto out;
+       }
 
-       if (delete_ref(logmsg, oldrefname, orig_sha1, REF_NODEREF)) {
+       if (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
+               ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
+                           oldrefname, strerror(errno));
+               goto out;
+       }
+
+       if (refs_delete_ref(&refs->base, logmsg, oldrefname,
+                           orig_sha1, REF_NODEREF)) {
                error("unable to delete old %s", oldrefname);
                goto rollback;
        }
@@ -2608,14 +2697,16 @@ static int files_rename_ref(struct ref_store *ref_store,
         * the safety anyway; we want to delete the reference whatever
         * its current value.
         */
-       if (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-                          sha1, NULL) &&
-           delete_ref(NULL, newrefname, NULL, REF_NODEREF)) {
+       if (!refs_read_ref_full(&refs->base, newrefname,
+                               RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
+                               sha1, NULL) &&
+           refs_delete_ref(&refs->base, NULL, newrefname,
+                           NULL, REF_NODEREF)) {
                if (errno == EISDIR) {
                        struct strbuf path = STRBUF_INIT;
                        int result;
 
-                       strbuf_git_path(&path, "%s", newrefname);
+                       files_ref_path(refs, &path, newrefname);
                        result = remove_empty_directories(&path);
                        strbuf_release(&path);
 
@@ -2629,7 +2720,7 @@ static int files_rename_ref(struct ref_store *ref_store,
                }
        }
 
-       if (log && rename_tmp_log(newrefname))
+       if (log && rename_tmp_log(refs, newrefname))
                goto rollback;
 
        logmoved = log;
@@ -2650,7 +2741,8 @@ static int files_rename_ref(struct ref_store *ref_store,
                goto rollback;
        }
 
-       return 0;
+       ret = 0;
+       goto out;
 
  rollback:
        lock = lock_ref_sha1_basic(refs, oldrefname, NULL, NULL, NULL,
@@ -2671,15 +2763,20 @@ static int files_rename_ref(struct ref_store *ref_store,
        log_all_ref_updates = flag;
 
  rollbacklog:
-       if (logmoved && rename(git_path("logs/%s", newrefname), git_path("logs/%s", oldrefname)))
+       if (logmoved && rename(sb_newref.buf, sb_oldref.buf))
                error("unable to restore logfile %s from %s: %s",
                        oldrefname, newrefname, strerror(errno));
        if (!logmoved && log &&
-           rename(git_path(TMP_RENAMED_LOG), git_path("logs/%s", oldrefname)))
-               error("unable to restore logfile %s from "TMP_RENAMED_LOG": %s",
+           rename(tmp_renamed_log.buf, sb_oldref.buf))
+               error("unable to restore logfile %s from logs/"TMP_RENAMED_LOG": %s",
                        oldrefname, strerror(errno));
+       ret = 1;
+ out:
+       strbuf_release(&sb_newref);
+       strbuf_release(&sb_oldref);
+       strbuf_release(&tmp_renamed_log);
 
-       return 1;
+       return ret;
 }
 
 static int close_ref(struct ref_lock *lock)
@@ -2738,10 +2835,15 @@ static int open_or_create_logfile(const char *path, void *cb)
  * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and
  * return -1.
  */
-static int log_ref_setup(const char *refname, int force_create,
+static int log_ref_setup(struct files_ref_store *refs,
+                        const char *refname, int force_create,
                         int *logfd, struct strbuf *err)
 {
-       char *logfile = git_pathdup("logs/%s", refname);
+       struct strbuf logfile_sb = STRBUF_INIT;
+       char *logfile;
+
+       files_reflog_path(refs, &logfile_sb, refname);
+       logfile = strbuf_detach(&logfile_sb, NULL);
 
        if (force_create || should_autocreate_reflog(refname)) {
                if (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {
@@ -2791,12 +2893,11 @@ static int files_create_reflog(struct ref_store *ref_store,
                               const char *refname, int force_create,
                               struct strbuf *err)
 {
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_WRITE, "create_reflog");
        int fd;
 
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "create_reflog");
-
-       if (log_ref_setup(refname, force_create, &fd, err))
+       if (log_ref_setup(refs, refname, force_create, &fd, err))
                return -1;
 
        if (fd >= 0)
@@ -2831,16 +2932,18 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
        return 0;
 }
 
-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
-                       const unsigned char *new_sha1, const char *msg,
-                       int flags, struct strbuf *err)
+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,
+                              int flags, struct strbuf *err)
 {
        int logfd, result;
 
        if (log_all_ref_updates == LOG_REFS_UNSET)
                log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
 
-       result = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,
+       result = log_ref_setup(refs, refname,
+                              flags & REF_FORCE_CREATE_REFLOG,
                               &logfd, err);
 
        if (result)
@@ -2851,18 +2954,24 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
+               struct strbuf sb = STRBUF_INIT;
                int save_errno = errno;
 
+               files_reflog_path(refs, &sb, refname);
                strbuf_addf(err, "unable to append to '%s': %s",
-                           git_path("logs/%s", refname), strerror(save_errno));
+                           sb.buf, strerror(save_errno));
+               strbuf_release(&sb);
                close(logfd);
                return -1;
        }
        if (close(logfd)) {
+               struct strbuf sb = STRBUF_INIT;
                int save_errno = errno;
 
+               files_reflog_path(refs, &sb, refname);
                strbuf_addf(err, "unable to append to '%s': %s",
-                           git_path("logs/%s", refname), strerror(save_errno));
+                           sb.buf, strerror(save_errno));
+               strbuf_release(&sb);
                return -1;
        }
        return 0;
@@ -2920,7 +3029,8 @@ static int commit_ref_update(struct files_ref_store *refs,
        files_assert_main_repository(refs, "commit_ref_update");
 
        clear_loose_ref_cache(refs);
-       if (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,
+       if (files_log_ref_write(refs, lock->ref_name,
+                               lock->old_oid.hash, sha1,
                                logmsg, 0, err)) {
                char *old_msg = strbuf_detach(err, NULL);
                strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -2947,13 +3057,15 @@ static int commit_ref_update(struct files_ref_store *refs,
                int head_flag;
                const char *head_ref;
 
-               head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
-                                             head_sha1, &head_flag);
+               head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD",
+                                                  RESOLVE_REF_READING,
+                                                  head_sha1, &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("HEAD", lock->old_oid.hash, sha1,
-                                         logmsg, 0, &log_err)) {
+                       if (files_log_ref_write(refs, "HEAD",
+                                               lock->old_oid.hash, sha1,
+                                               logmsg, 0, &log_err)) {
                                error("%s", log_err.buf);
                                strbuf_release(&log_err);
                        }
@@ -2985,24 +3097,28 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
        return ret;
 }
 
-static void update_symref_reflog(struct ref_lock *lock, const char *refname,
+static void update_symref_reflog(struct files_ref_store *refs,
+                                struct ref_lock *lock, const char *refname,
                                 const char *target, const char *logmsg)
 {
        struct strbuf err = STRBUF_INIT;
        unsigned char new_sha1[20];
-       if (logmsg && !read_ref(target, new_sha1) &&
-           files_log_ref_write(refname, lock->old_oid.hash, new_sha1,
-                               logmsg, 0, &err)) {
+       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)) {
                error("%s", err.buf);
                strbuf_release(&err);
        }
 }
 
-static int create_symref_locked(struct ref_lock *lock, const char *refname,
+static int create_symref_locked(struct files_ref_store *refs,
+                               struct ref_lock *lock, const char *refname,
                                const char *target, const char *logmsg)
 {
        if (prefer_symlink_refs && !create_ref_symlink(lock, target)) {
-               update_symref_reflog(lock, refname, target, logmsg);
+               update_symref_reflog(refs, lock, refname, target, logmsg);
                return 0;
        }
 
@@ -3010,7 +3126,7 @@ static int create_symref_locked(struct ref_lock *lock, const char *refname,
                return error("unable to fdopen %s: %s",
                             lock->lk->tempfile.filename.buf, strerror(errno));
 
-       update_symref_reflog(lock, refname, target, logmsg);
+       update_symref_reflog(refs, lock, refname, target, logmsg);
 
        /* no error check; commit_ref will check ferror */
        fprintf(lock->lk->tempfile.fp, "ref: %s\n", target);
@@ -3025,7 +3141,7 @@ static int files_create_symref(struct ref_store *ref_store,
                               const char *logmsg)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 0, "create_symref");
+               files_downcast(ref_store, REF_STORE_WRITE, "create_symref");
        struct strbuf err = STRBUF_INIT;
        struct ref_lock *lock;
        int ret;
@@ -3039,13 +3155,22 @@ static int files_create_symref(struct ref_store *ref_store,
                return -1;
        }
 
-       ret = create_symref_locked(lock, refname, target, logmsg);
+       ret = create_symref_locked(refs, lock, refname, target, logmsg);
        unlock_ref(lock);
        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;
@@ -3072,7 +3197,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char
        lock->lk = &head_lock;
        lock->ref_name = xstrdup(head_rel);
 
-       ret = create_symref_locked(lock, head_rel, target, logmsg);
+       ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
 
        unlock_ref(lock); /* will free lock */
        strbuf_release(&head_path);
@@ -3082,22 +3207,30 @@ int set_worktree_head_symref(const char *gitdir, const char *target, const char
 static int files_reflog_exists(struct ref_store *ref_store,
                               const char *refname)
 {
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ, "reflog_exists");
+       struct strbuf sb = STRBUF_INIT;
        struct stat st;
+       int ret;
 
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "reflog_exists");
-
-       return !lstat(git_path("logs/%s", refname), &st) &&
-               S_ISREG(st.st_mode);
+       files_reflog_path(refs, &sb, refname);
+       ret = !lstat(sb.buf, &st) && S_ISREG(st.st_mode);
+       strbuf_release(&sb);
+       return ret;
 }
 
 static int files_delete_reflog(struct ref_store *ref_store,
                               const char *refname)
 {
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "delete_reflog");
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_WRITE, "delete_reflog");
+       struct strbuf sb = STRBUF_INIT;
+       int ret;
 
-       return remove_path(git_path("logs/%s", refname));
+       files_reflog_path(refs, &sb, refname);
+       ret = remove_path(sb.buf);
+       strbuf_release(&sb);
+       return ret;
 }
 
 static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
@@ -3145,22 +3278,24 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
                                             each_reflog_ent_fn fn,
                                             void *cb_data)
 {
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ,
+                              "for_each_reflog_ent_reverse");
        struct strbuf sb = STRBUF_INIT;
        FILE *logfp;
        long pos;
        int ret = 0, at_tail = 1;
 
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "for_each_reflog_ent_reverse");
-
-       logfp = fopen(git_path("logs/%s", refname), "r");
+       files_reflog_path(refs, &sb, refname);
+       logfp = fopen(sb.buf, "r");
+       strbuf_release(&sb);
        if (!logfp)
                return -1;
 
        /* Jump to the end */
        if (fseek(logfp, 0, SEEK_END) < 0)
-               return error("cannot seek back reflog for %s: %s",
-                            refname, strerror(errno));
+               ret = error("cannot seek back reflog for %s: %s",
+                           refname, strerror(errno));
        pos = ftell(logfp);
        while (!ret && 0 < pos) {
                int cnt;
@@ -3170,13 +3305,17 @@ static int files_for_each_reflog_ent_reverse(struct ref_store *ref_store,
 
                /* Fill next block from the end */
                cnt = (sizeof(buf) < pos) ? sizeof(buf) : pos;
-               if (fseek(logfp, pos - cnt, SEEK_SET))
-                       return error("cannot seek back reflog for %s: %s",
-                                    refname, strerror(errno));
+               if (fseek(logfp, pos - cnt, SEEK_SET)) {
+                       ret = error("cannot seek back reflog for %s: %s",
+                                   refname, strerror(errno));
+                       break;
+               }
                nread = fread(buf, cnt, 1, logfp);
-               if (nread != 1)
-                       return error("cannot read %d bytes from reflog for %s: %s",
-                                    cnt, refname, strerror(errno));
+               if (nread != 1) {
+                       ret = error("cannot read %d bytes from reflog for %s: %s",
+                                   cnt, refname, strerror(errno));
+                       break;
+               }
                pos -= cnt;
 
                scanp = endp = buf + cnt;
@@ -3252,14 +3391,16 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
                                     const char *refname,
                                     each_reflog_ent_fn fn, void *cb_data)
 {
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ,
+                              "for_each_reflog_ent");
        FILE *logfp;
        struct strbuf sb = STRBUF_INIT;
        int ret = 0;
 
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "for_each_reflog_ent");
-
-       logfp = fopen(git_path("logs/%s", refname), "r");
+       files_reflog_path(refs, &sb, refname);
+       logfp = fopen(sb.buf, "r");
+       strbuf_release(&sb);
        if (!logfp)
                return -1;
 
@@ -3273,6 +3414,7 @@ static int files_for_each_reflog_ent(struct ref_store *ref_store,
 struct files_reflog_iterator {
        struct ref_iterator base;
 
+       struct ref_store *ref_store;
        struct dir_iterator *dir_iterator;
        struct object_id oid;
 };
@@ -3294,8 +3436,9 @@ static int files_reflog_iterator_advance(struct ref_iterator *ref_iterator)
                if (ends_with(diter->basename, ".lock"))
                        continue;
 
-               if (read_ref_full(diter->relative_path, 0,
-                                 iter->oid.hash, &flags)) {
+               if (refs_read_ref_full(iter->ref_store,
+                                      diter->relative_path, 0,
+                                      iter->oid.hash, &flags)) {
                        error("bad ref for %s", diter->path.buf);
                        continue;
                }
@@ -3339,14 +3482,18 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
 
 static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
 {
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ,
+                              "reflog_iterator_begin");
        struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
        struct ref_iterator *ref_iterator = &iter->base;
-
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "reflog_iterator_begin");
+       struct strbuf sb = STRBUF_INIT;
 
        base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
-       iter->dir_iterator = dir_iterator_begin(git_path("logs"));
+       files_reflog_path(refs, &sb, NULL);
+       iter->dir_iterator = dir_iterator_begin(sb.buf);
+       iter->ref_store = ref_store;
+       strbuf_release(&sb);
        return ref_iterator;
 }
 
@@ -3585,8 +3732,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                         * the transaction, so we have to read it here
                         * to record and possibly check old_sha1:
                         */
-                       if (read_ref_full(referent.buf, 0,
-                                         lock->old_oid.hash, NULL)) {
+                       if (refs_read_ref_full(&refs->base,
+                                              referent.buf, 0,
+                                              lock->old_oid.hash, NULL)) {
                                if (update->flags & REF_HAVE_OLD) {
                                        strbuf_addf(err, "cannot lock ref '%s': "
                                                    "error reading reference",
@@ -3676,7 +3824,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
                                    struct strbuf *err)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 0, "ref_transaction_commit");
+               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;
@@ -3684,6 +3833,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
        char *head_ref = NULL;
        int head_type;
        struct object_id head_oid;
+       struct strbuf sb = STRBUF_INIT;
 
        assert(err);
 
@@ -3738,8 +3888,9 @@ static int files_transaction_commit(struct ref_store *ref_store,
         * head_ref within the transaction, then split_head_update()
         * arranges for the reflog of HEAD to be updated, too.
         */
-       head_ref = resolve_refdup("HEAD", RESOLVE_REF_NO_RECURSE,
-                                 head_oid.hash, &head_type);
+       head_ref = refs_resolve_refdup(ref_store, "HEAD",
+                                      RESOLVE_REF_NO_RECURSE,
+                                      head_oid.hash, &head_type);
 
        if (head_ref && !(head_type & REF_ISSYMREF)) {
                free(head_ref);
@@ -3768,7 +3919,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
 
                if (update->flags & REF_NEEDS_COMMIT ||
                    update->flags & REF_LOG_ONLY) {
-                       if (files_log_ref_write(lock->ref_name,
+                       if (files_log_ref_write(refs,
+                                               lock->ref_name,
                                                lock->old_oid.hash,
                                                update->new_sha1,
                                                update->msg, update->flags,
@@ -3805,7 +3957,9 @@ static int files_transaction_commit(struct ref_store *ref_store,
                        if (!(update->type & REF_ISPACKED) ||
                            update->type & REF_ISSYMREF) {
                                /* It is a loose reference. */
-                               if (unlink_or_msg(git_path("%s", lock->ref_name), err)) {
+                               strbuf_reset(&sb);
+                               files_ref_path(refs, &sb, lock->ref_name);
+                               if (unlink_or_msg(sb.buf, err)) {
                                        ret = TRANSACTION_GENERIC_ERROR;
                                        goto cleanup;
                                }
@@ -3825,14 +3979,17 @@ static int files_transaction_commit(struct ref_store *ref_store,
 
        /* Delete the reflogs of any references that were deleted: */
        for_each_string_list_item(ref_to_delete, &refs_to_delete) {
-               if (!unlink_or_warn(git_path("logs/%s", ref_to_delete->string)))
-                       try_remove_empty_parents(ref_to_delete->string,
+               strbuf_reset(&sb);
+               files_reflog_path(refs, &sb, ref_to_delete->string);
+               if (!unlink_or_warn(sb.buf))
+                       try_remove_empty_parents(refs, ref_to_delete->string,
                                                 REMOVE_EMPTY_PARENTS_REFLOG);
        }
 
        clear_loose_ref_cache(refs);
 
 cleanup:
+       strbuf_release(&sb);
        transaction->state = REF_TRANSACTION_CLOSED;
 
        for (i = 0; i < transaction->nr; i++) {
@@ -3849,7 +4006,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
                         * can only work because we have already
                         * removed the lockfile.)
                         */
-                       try_remove_empty_parents(update->refname,
+                       try_remove_empty_parents(refs, update->refname,
                                                 REMOVE_EMPTY_PARENTS_REF);
                }
        }
@@ -3874,7 +4031,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
                                            struct strbuf *err)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 0, "initial_ref_transaction_commit");
+               files_downcast(ref_store, REF_STORE_WRITE,
+                              "initial_ref_transaction_commit");
        int ret = 0, i;
        struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
 
@@ -3905,7 +4063,8 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
         * so here we really only check that none of the references
         * that we are creating already exists.
         */
-       if (for_each_rawref(ref_present, &affected_refnames))
+       if (refs_for_each_rawref(&refs->base, ref_present,
+                                &affected_refnames))
                die("BUG: initial ref transaction called with existing refs");
 
        for (i = 0; i < transaction->nr; i++) {
@@ -3914,9 +4073,9 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
                if ((update->flags & REF_HAVE_OLD) &&
                    !is_null_sha1(update->old_sha1))
                        die("BUG: initial ref transaction with old_sha1 set");
-               if (verify_refname_available(update->refname,
-                                            &affected_refnames, NULL,
-                                            err)) {
+               if (refs_verify_refname_available(&refs->base, update->refname,
+                                                 &affected_refnames, NULL,
+                                                 err)) {
                        ret = TRANSACTION_NAME_CONFLICT;
                        goto cleanup;
                }
@@ -3996,10 +4155,11 @@ static int files_reflog_expire(struct ref_store *ref_store,
                               void *policy_cb_data)
 {
        struct files_ref_store *refs =
-               files_downcast(ref_store, 0, "reflog_expire");
+               files_downcast(ref_store, REF_STORE_WRITE, "reflog_expire");
        static struct lock_file reflog_lock;
        struct expire_reflog_cb cb;
        struct ref_lock *lock;
+       struct strbuf log_file_sb = STRBUF_INIT;
        char *log_file;
        int status = 0;
        int type;
@@ -4023,12 +4183,13 @@ static int files_reflog_expire(struct ref_store *ref_store,
                strbuf_release(&err);
                return -1;
        }
-       if (!reflog_exists(refname)) {
+       if (!refs_reflog_exists(ref_store, refname)) {
                unlock_ref(lock);
                return 0;
        }
 
-       log_file = git_pathdup("logs/%s", refname);
+       files_reflog_path(refs, &log_file_sb, refname);
+       log_file = strbuf_detach(&log_file_sb, NULL);
        if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
                /*
                 * Even though holding $GIT_DIR/logs/$reflog.lock has
@@ -4053,7 +4214,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
        }
 
        (*prepare_fn)(refname, sha1, cb.policy_cb);
-       for_each_reflog_ent(refname, expire_reflog_ent, &cb);
+       refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);
        (*cleanup_fn)(cb.policy_cb);
 
        if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
@@ -4099,18 +4260,21 @@ static int files_reflog_expire(struct ref_store *ref_store,
 
 static int files_init_db(struct ref_store *ref_store, struct strbuf *err)
 {
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "init_db");
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_WRITE, "init_db");
+       struct strbuf sb = STRBUF_INIT;
 
        /*
         * Create .git/refs/{heads,tags}
         */
-       safe_create_dir(git_path("refs/heads"), 1);
-       safe_create_dir(git_path("refs/tags"), 1);
-       if (get_shared_repository()) {
-               adjust_shared_perm(git_path("refs/heads"));
-               adjust_shared_perm(git_path("refs/tags"));
-       }
+       files_ref_path(refs, &sb, "refs/heads");
+       safe_create_dir(sb.buf, 1);
+
+       strbuf_reset(&sb);
+       files_ref_path(refs, &sb, "refs/tags");
+       safe_create_dir(sb.buf, 1);
+
+       strbuf_release(&sb);
        return 0;
 }
 
index fa93c9a32ec4c52b9cd6597b9c2ebb4b4501206b..690498698e41b75cf7a194ac03cafd778f0f19f1 100644 (file)
@@ -111,28 +111,6 @@ enum peel_status {
  */
 enum peel_status peel_object(const unsigned char *name, unsigned char *sha1);
 
-/*
- * Return 0 if a reference named refname could be created without
- * conflicting with the name of an existing reference. Otherwise,
- * return a negative value and write an explanation to err. If extras
- * is non-NULL, it is a list of additional refnames with which refname
- * is not allowed to conflict. If skip is non-NULL, ignore potential
- * conflicts with refs in skip (e.g., because they are scheduled for
- * deletion in the same operation). Behavior is undefined if the same
- * name is listed in both extras and skip.
- *
- * Two reference names conflict if one of them exactly matches the
- * leading components of the other; e.g., "foo/bar" conflicts with
- * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
- * "foo/barbados".
- *
- * extras and skip must be sorted.
- */
-int verify_refname_available(const char *newname,
-                            const struct string_list *extras,
-                            const struct string_list *skip,
-                            struct strbuf *err);
-
 /*
  * Copy the reflog message msg to buf, which has been allocated sufficiently
  * large, while cleaning up the whitespaces.  Especially, convert LF to space,
@@ -222,16 +200,13 @@ enum ref_transaction_state {
  * as atomically as possible.  This structure is opaque to callers.
  */
 struct ref_transaction {
+       struct ref_store *ref_store;
        struct ref_update **updates;
        size_t alloc;
        size_t nr;
        enum ref_transaction_state state;
 };
 
-int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
-                       const unsigned char *new_sha1, const char *msg,
-                       int flags, struct strbuf *err);
-
 /*
  * Check for entries in extras that are within the specified
  * directory, where dirname is a reference directory name including
@@ -256,7 +231,9 @@ const char *find_descendant_ref(const char *dirname,
  * processes (though rename_ref() catches some races that might get by
  * this check).
  */
-int rename_ref_available(const char *old_refname, const char *new_refname);
+int refs_rename_ref_available(struct ref_store *refs,
+                             const char *old_refname,
+                             const char *new_refname);
 
 /* We allow "recursive" symbolic refs. Only within reason, though */
 #define SYMREF_MAXDEPTH 5
@@ -485,13 +462,19 @@ struct ref_store;
 
 /* refs backends */
 
+/* ref_store_init flags */
+#define REF_STORE_READ         (1 << 0)
+#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)
+
 /*
- * Initialize the ref_store for the specified submodule, or for the
- * main repository if submodule == NULL. These functions should call
- * base_ref_store_init() to initialize the shared part of the
- * ref_store and to record the ref_store for later lookup.
+ * Initialize the ref_store for the specified gitdir. These functions
+ * should call base_ref_store_init() to initialize the shared part of
+ * the ref_store and to record the ref_store for later lookup.
  */
-typedef struct ref_store *ref_store_init_fn(const char *submodule);
+typedef struct ref_store *ref_store_init_fn(const char *gitdir,
+                                           unsigned int flags);
 
 typedef int ref_init_db_fn(struct ref_store *refs, struct strbuf *err);
 
@@ -644,21 +627,4 @@ struct ref_store {
 void base_ref_store_init(struct ref_store *refs,
                         const struct ref_storage_be *be);
 
-/*
- * Return the ref_store instance for the specified submodule. For the
- * main repository, use submodule==NULL; such a call cannot fail. For
- * a submodule, the submodule must exist and be a nonbare repository,
- * otherwise return NULL. If the requested reference store has not yet
- * been initialized, initialize it first.
- *
- * For backwards compatibility, submodule=="" is treated the same as
- * submodule==NULL.
- */
-struct ref_store *get_ref_store(const char *submodule);
-
-const char *resolve_ref_recursively(struct ref_store *refs,
-                                   const char *refname,
-                                   int resolve_flags,
-                                   unsigned char *sha1, int *flags);
-
 #endif /* REFS_REFS_INTERNAL_H */
index 34a97e7328d440d48badd53bb0d3167a58558ee9..ece45993dac582ddc6ba19b2362ba1716e075eae 100644 (file)
@@ -22,6 +22,7 @@ struct options {
        unsigned long depth;
        char *deepen_since;
        struct string_list deepen_not;
+       struct string_list push_options;
        unsigned progress : 1,
                check_self_contained_and_connected : 1,
                cloning : 1,
@@ -139,6 +140,9 @@ static int set_option(const char *name, const char *value)
                else
                        return -1;
                return 0;
+       } else if (!strcmp(name, "push-option")) {
+               string_list_append(&options.push_options, value);
+               return 0;
 
 #if LIBCURL_VERSION_NUM >= 0x070a08
        } else if (!strcmp(name, "family")) {
@@ -163,7 +167,7 @@ struct discovery {
        char *buf;
        size_t len;
        struct ref *refs;
-       struct sha1_array shallow;
+       struct oid_array shallow;
        unsigned proto_git : 1;
 };
 static struct discovery *last_discovery;
@@ -230,7 +234,7 @@ static void free_discovery(struct discovery *d)
        if (d) {
                if (d == last_discovery)
                        last_discovery = NULL;
-               free(d->shallow.sha1);
+               free(d->shallow.oid);
                free(d->buf_alloc);
                free_refs(d->refs);
                free(d);
@@ -527,6 +531,12 @@ static int probe_rpc(struct rpc_state *rpc, struct slot_results *results)
        return err;
 }
 
+static curl_off_t xcurl_off_t(ssize_t len) {
+       if (len > maximum_signed_value_of_type(curl_off_t))
+               die("cannot handle pushes this big");
+       return (curl_off_t) len;
+}
+
 static int post_rpc(struct rpc_state *rpc)
 {
        struct active_request_slot *slot;
@@ -610,7 +620,7 @@ static int post_rpc(struct rpc_state *rpc)
                 * and we just need to send it.
                 */
                curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
-               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE_LARGE, xcurl_off_t(gzip_size));
 
        } else if (use_gzip && 1024 < rpc->len) {
                /* The client backend isn't giving us compressed data so
@@ -641,7 +651,7 @@ static int post_rpc(struct rpc_state *rpc)
 
                headers = curl_slist_append(headers, "Content-Encoding: gzip");
                curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, gzip_body);
-               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, gzip_size);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE_LARGE, xcurl_off_t(gzip_size));
 
                if (options.verbosity > 1) {
                        fprintf(stderr, "POST %s (gzip %lu to %lu bytes)\n",
@@ -654,7 +664,7 @@ static int post_rpc(struct rpc_state *rpc)
                 * more normal Content-Length approach.
                 */
                curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDS, rpc->buf);
-               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE, rpc->len);
+               curl_easy_setopt(slot->curl, CURLOPT_POSTFIELDSIZE_LARGE, xcurl_off_t(rpc->len));
                if (options.verbosity > 1) {
                        fprintf(stderr, "POST %s (%lu bytes)\n",
                                rpc->service_name, (unsigned long)rpc->len);
@@ -943,6 +953,9 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
                argv_array_push(&args, "--quiet");
        else if (options.verbosity > 1)
                argv_array_push(&args, "--verbose");
+       for (i = 0; i < options.push_options.nr; i++)
+               argv_array_pushf(&args, "--push-option=%s",
+                                options.push_options.items[i].string);
        argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
        for_each_string_list_item(cas_option, &cas_options)
                argv_array_push(&args, cas_option->string);
@@ -1028,6 +1041,7 @@ int cmd_main(int argc, const char **argv)
        options.progress = !!isatty(2);
        options.thin = 1;
        string_list_init(&options.deepen_not, 1);
+       string_list_init(&options.push_options, 1);
 
        remote = remote_get(argv[1]);
 
index 9f83fe2c4cbd477e47fc6c81eab324be7aa088c6..801137c72ebb2edf196811a8031db603c61f376d 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -630,7 +630,7 @@ struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
        return parse_refspec_internal(nr_refspec, refspec, 1, 0);
 }
 
-static struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
 {
        return parse_refspec_internal(nr_refspec, refspec, 0, 0);
 }
@@ -2279,7 +2279,7 @@ static struct push_cas *add_cas_entry(struct push_cas_option *cas,
        return entry;
 }
 
-int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
+static int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
 {
        const char *colon;
        struct push_cas *entry;
index dd8c5175776baabbac2bd735e21bc859fb62a111..6c28cd3e4bfe2e8be058485d0b963b23622b999f 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -149,11 +149,11 @@ int check_ref_type(const struct ref *ref, int flags);
  */
 void free_refs(struct ref *ref);
 
-struct sha1_array;
+struct oid_array;
 extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                                     struct ref **list, unsigned int flags,
-                                    struct sha1_array *extra_have,
-                                    struct sha1_array *shallow);
+                                    struct oid_array *extra_have,
+                                    struct oid_array *shallow);
 
 int resolve_remote_symref(struct ref *ref, struct ref *list);
 int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);
@@ -169,6 +169,7 @@ struct ref *ref_remove_duplicates(struct ref *ref_map);
 
 int valid_fetch_refspec(const char *refspec);
 struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec);
+extern struct refspec *parse_push_refspec(int nr_refspec, const char **refspec);
 
 void free_refspec(int nr_refspec, struct refspec *refspec);
 
@@ -290,7 +291,6 @@ struct push_cas_option {
 };
 
 extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
-extern int parse_push_cas_option(struct push_cas_option *, const char *arg, int unset);
 
 extern int is_empty_cas(const struct push_cas_option *);
 void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *);
index 9fac1a607de6470ab0c9b7e5079e8d36c8d68c75..14886ec92b4f67527a86fc821998214a4d2b321f 100644 (file)
@@ -259,8 +259,6 @@ extern void put_revision_mark(const struct rev_info *revs,
 extern void mark_parents_uninteresting(struct commit *commit);
 extern void mark_tree_uninteresting(struct tree *tree);
 
-char *path_name(struct strbuf *path, const char *name);
-
 extern void show_object_with_name(FILE *, struct object *, const char *);
 
 extern void add_pending_object(struct rev_info *revs,
index 5227f78aeaae7e76bfc85d4d7ca2886b2b52215e..574b81d3e82bbe6de31b141c3951ab408daad5e6 100644 (file)
@@ -48,7 +48,7 @@ static void cleanup_children(int sig, int in_signal)
 
                kill(p->pid, sig);
 
-               if (p->process->wait_after_clean) {
+               if (p->process && p->process->wait_after_clean) {
                        p->next = children_to_wait_for;
                        children_to_wait_for = p;
                } else {
index d2d2a49a0231293e98ab8546945f8d3ef9fa205d..78bb34ebec297102c852a5b88ec7b4f10ffbc1d8 100644 (file)
@@ -50,7 +50,7 @@ static void feed_object(const unsigned char *sha1, FILE *fh, int negative)
 /*
  * Make a pack stream and spit it out into file descriptor fd
  */
-static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, struct send_pack_args *args)
+static int pack_objects(int fd, struct ref *refs, struct oid_array *extra, struct send_pack_args *args)
 {
        /*
         * The child becomes pack-objects --revs; we feed
@@ -98,7 +98,7 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
         */
        po_in = xfdopen(po.in, "w");
        for (i = 0; i < extra->nr; i++)
-               feed_object(extra->sha1[i], po_in, 1);
+               feed_object(extra->oid[i].hash, po_in, 1);
 
        while (refs) {
                if (!is_null_oid(&refs->old_oid))
@@ -376,7 +376,7 @@ static void reject_invalid_nonce(const char *nonce, int len)
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
              struct ref *remote_refs,
-             struct sha1_array *extra_have)
+             struct oid_array *extra_have)
 {
        int in = fd[0];
        int out = fd[1];
@@ -532,6 +532,14 @@ int send_pack(struct send_pack_args *args,
                }
        }
 
+       if (use_push_options) {
+               struct string_list_item *item;
+
+               packet_buf_flush(&req_buf);
+               for_each_string_list_item(item, args->push_options)
+                       packet_buf_write(&req_buf, "%s", item->string);
+       }
+
        if (args->stateless_rpc) {
                if (!args->dry_run && (cmds_sent || is_repository_shallow())) {
                        packet_buf_flush(&req_buf);
@@ -544,18 +552,6 @@ int send_pack(struct send_pack_args *args,
        strbuf_release(&req_buf);
        strbuf_release(&cap_buf);
 
-       if (use_push_options) {
-               struct string_list_item *item;
-               struct strbuf sb = STRBUF_INIT;
-
-               for_each_string_list_item(item, args->push_options)
-                       packet_buf_write(&sb, "%s", item->string);
-
-               write_or_die(out, sb.buf, sb.len);
-               packet_flush(out);
-               strbuf_release(&sb);
-       }
-
        if (use_sideband && cmds_sent) {
                memset(&demux, 0, sizeof(demux));
                demux.proc = sideband_demux;
index 67fc40f4ec1a0847fb16535334e07bf196e8028a..6af71f7008127df0acb72cfaf3b679f7ef332a4e 100644 (file)
@@ -32,6 +32,6 @@ int option_parse_push_signed(const struct option *opt,
 
 int send_pack(struct send_pack_args *args,
              int fd[], struct child_process *conn,
-             struct ref *remote_refs, struct sha1_array *extra_have);
+             struct ref *remote_refs, struct oid_array *extra_have);
 
 #endif
index 1f729b053bbc6f544fe0b3e8736113082894b24d..77afecaebf0980bec4f938b15bd08ae35ba09d83 100644 (file)
@@ -602,6 +602,12 @@ N_("you have staged changes in your working tree\n"
 "\n"
 "  git rebase --continue\n");
 
+#define ALLOW_EMPTY (1<<0)
+#define EDIT_MSG    (1<<1)
+#define AMEND_MSG   (1<<2)
+#define CLEANUP_MSG (1<<3)
+#define VERIFY_MSG  (1<<4)
+
 /*
  * If we are cherry-pick, and if the merge did not result in
  * hand-editing, we will hit this commit and inherit the original
@@ -615,8 +621,7 @@ N_("you have staged changes in your working tree\n"
  * author metadata.
  */
 static int run_git_commit(const char *defmsg, struct replay_opts *opts,
-                         int allow_empty, int edit, int amend,
-                         int cleanup_commit_message)
+                         unsigned int flags)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
        const char *value;
@@ -624,7 +629,7 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
        cmd.git_cmd = 1;
 
        if (is_rebase_i(opts)) {
-               if (!edit) {
+               if (!(flags & EDIT_MSG)) {
                        cmd.stdout_to_stderr = 1;
                        cmd.err = -1;
                }
@@ -638,9 +643,10 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
        }
 
        argv_array_push(&cmd.args, "commit");
-       argv_array_push(&cmd.args, "-n");
 
-       if (amend)
+       if (!(flags & VERIFY_MSG))
+               argv_array_push(&cmd.args, "-n");
+       if ((flags & AMEND_MSG))
                argv_array_push(&cmd.args, "--amend");
        if (opts->gpg_sign)
                argv_array_pushf(&cmd.args, "-S%s", opts->gpg_sign);
@@ -648,16 +654,16 @@ static int run_git_commit(const char *defmsg, struct replay_opts *opts,
                argv_array_push(&cmd.args, "-s");
        if (defmsg)
                argv_array_pushl(&cmd.args, "-F", defmsg, NULL);
-       if (cleanup_commit_message)
+       if ((flags & CLEANUP_MSG))
                argv_array_push(&cmd.args, "--cleanup=strip");
-       if (edit)
+       if ((flags & EDIT_MSG))
                argv_array_push(&cmd.args, "-e");
-       else if (!cleanup_commit_message &&
+       else if (!(flags & CLEANUP_MSG) &&
                 !opts->signoff && !opts->record_origin &&
                 git_config_get_value("commit.cleanup", &value))
                argv_array_push(&cmd.args, "--cleanup=verbatim");
 
-       if (allow_empty)
+       if ((flags & ALLOW_EMPTY))
                argv_array_push(&cmd.args, "--allow-empty");
 
        if (opts->allow_empty_message)
@@ -926,14 +932,14 @@ static void record_in_rewritten(struct object_id *oid,
 static int do_pick_commit(enum todo_command command, struct commit *commit,
                struct replay_opts *opts, int final_fixup)
 {
-       int edit = opts->edit, cleanup_commit_message = 0;
-       const char *msg_file = edit ? NULL : git_path_merge_msg();
+       unsigned int flags = opts->edit ? EDIT_MSG : 0;
+       const char *msg_file = opts->edit ? NULL : git_path_merge_msg();
        unsigned char head[20];
        struct commit *base, *next, *parent;
        const char *base_label, *next_label;
        struct commit_message msg = { NULL, NULL, NULL, NULL };
        struct strbuf msgbuf = STRBUF_INIT;
-       int res, unborn = 0, amend = 0, allow = 0;
+       int res, unborn = 0, allow;
 
        if (opts->no_commit) {
                /*
@@ -991,7 +997,9 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        opts);
                if (res || command != TODO_REWORD)
                        goto leave;
-               edit = amend = 1;
+               flags |= EDIT_MSG | AMEND_MSG;
+               if (command == TODO_REWORD)
+                       flags |= VERIFY_MSG;
                msg_file = NULL;
                goto fast_forward_edit;
        }
@@ -1046,15 +1054,15 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
        }
 
        if (command == TODO_REWORD)
-               edit = 1;
+               flags |= EDIT_MSG | VERIFY_MSG;
        else if (is_fixup(command)) {
                if (update_squash_messages(command, commit, opts))
                        return -1;
-               amend = 1;
+               flags |= AMEND_MSG;
                if (!final_fixup)
                        msg_file = rebase_path_squash_msg();
                else if (file_exists(rebase_path_fixup_msg())) {
-                       cleanup_commit_message = 1;
+                       flags |= CLEANUP_MSG;
                        msg_file = rebase_path_fixup_msg();
                } else {
                        const char *dest = git_path("SQUASH_MSG");
@@ -1064,7 +1072,7 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                                             rebase_path_squash_msg(), dest);
                        unlink(git_path("MERGE_MSG"));
                        msg_file = dest;
-                       edit = 1;
+                       flags |= EDIT_MSG;
                }
        }
 
@@ -1123,11 +1131,11 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
        if (allow < 0) {
                res = allow;
                goto leave;
-       }
+       } else if (allow)
+               flags |= ALLOW_EMPTY;
        if (!opts->no_commit)
 fast_forward_edit:
-               res = run_git_commit(msg_file, opts, allow, edit, amend,
-                                    cleanup_commit_message);
+               res = run_git_commit(msg_file, opts, flags);
 
        if (!res && final_fixup) {
                unlink(rebase_path_fixup_msg());
@@ -1997,7 +2005,8 @@ static int pick_commits(struct todo_list *todo_list, struct replay_opts *opts)
                        if (item->command == TODO_EDIT) {
                                struct commit *commit = item->commit;
                                if (!res)
-                                       warning(_("stopped at %s... %.*s"),
+                                       fprintf(stderr,
+                                               _("Stopped at %s...  %.*s\n"),
                                                short_commit_name(commit),
                                                item->arg_len, item->arg);
                                return error_with_patch(commit,
@@ -2153,7 +2162,7 @@ static int continue_single_pick(void)
 
 static int commit_staged_changes(struct replay_opts *opts)
 {
-       int amend = 0;
+       unsigned int flags = ALLOW_EMPTY | EDIT_MSG;
 
        if (has_unstaged_changes(1))
                return error(_("cannot rebase: You have unstaged changes."));
@@ -2183,10 +2192,10 @@ static int commit_staged_changes(struct replay_opts *opts)
                                       "--continue' again."));
 
                strbuf_release(&rev);
-               amend = 1;
+               flags |= AMEND_MSG;
        }
 
-       if (run_git_commit(rebase_path_message(), opts, 1, 1, amend, 0))
+       if (run_git_commit(rebase_path_message(), opts, flags))
                return error(_("could not commit staged changes."));
        unlink(rebase_path_amend());
        return 0;
index 7bc4e75d22ce099ca68aa1fac50c9123546ec5b7..f6c1a3dfb04bff5310a1b930b03478d88c9c2904 100644 (file)
@@ -14,19 +14,21 @@ static int update_info_file(char *path, int (*generate)(FILE *))
        char *tmp = mkpathdup("%s_XXXXXX", path);
        int ret = -1;
        int fd = -1;
-       FILE *fp = NULL;
+       FILE *fp = NULL, *to_close;
 
        safe_create_leading_directories(path);
        fd = git_mkstemp_mode(tmp, 0666);
        if (fd < 0)
                goto out;
-       fp = fdopen(fd, "w");
+       to_close = fp = fdopen(fd, "w");
        if (!fp)
                goto out;
+       fd = -1;
        ret = generate(fp);
        if (ret)
                goto out;
-       if (fclose(fp))
+       fp = NULL;
+       if (fclose(to_close))
                goto out;
        if (adjust_shared_perm(tmp) < 0)
                goto out;
diff --git a/setup.c b/setup.c
index 8f64fbdfb28fc2e487cfdba76561e5b3a25766f0..0309c278218f96cb6b11e6a9d60011efce54cf62 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -135,6 +135,7 @@ int path_inside_repo(const char *prefix, const char *path)
 int check_filename(const char *prefix, const char *arg)
 {
        const char *name;
+       char *to_free = NULL;
        struct stat st;
 
        if (starts_with(arg, ":/")) {
@@ -142,13 +143,17 @@ int check_filename(const char *prefix, const char *arg)
                        return 1;
                name = arg + 2;
        } else if (prefix)
-               name = prefix_filename(prefix, strlen(prefix), arg);
+               name = to_free = prefix_filename(prefix, arg);
        else
                name = arg;
-       if (!lstat(name, &st))
+       if (!lstat(name, &st)) {
+               free(to_free);
                return 1; /* file exists */
-       if (errno == ENOENT || errno == ENOTDIR)
+       }
+       if (errno == ENOENT || errno == ENOTDIR) {
+               free(to_free);
                return 0; /* file does not exist */
+       }
        die_errno("failed to stat '%s'", arg);
 }
 
@@ -531,6 +536,7 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
        ssize_t len;
 
        if (stat(path, &st)) {
+               /* NEEDSWORK: discern between ENOENT vs other errors */
                error_code = READ_GITFILE_ERR_STAT_FAILED;
                goto cleanup_return;
        }
@@ -721,8 +727,10 @@ static const char *setup_discovered_git_dir(const char *gitdir,
        if (offset == cwd->len)
                return NULL;
 
-       /* Make "offset" point to past the '/', and add a '/' at the end */
-       offset++;
+       /* Make "offset" point past the '/' (already the case for root dirs) */
+       if (offset != offset_1st_component(cwd->buf))
+               offset++;
+       /* Add a '/' at the end */
        strbuf_addch(cwd, '/');
        return cwd->buf + offset;
 }
@@ -816,50 +824,51 @@ static int canonicalize_ceiling_entry(struct string_list_item *item,
        }
 }
 
+enum discovery_result {
+       GIT_DIR_NONE = 0,
+       GIT_DIR_EXPLICIT,
+       GIT_DIR_DISCOVERED,
+       GIT_DIR_BARE,
+       /* these are errors */
+       GIT_DIR_HIT_CEILING = -1,
+       GIT_DIR_HIT_MOUNT_POINT = -2,
+       GIT_DIR_INVALID_GITFILE = -3
+};
+
 /*
  * We cannot decide in this function whether we are in the work tree or
  * not, since the config can only be read _after_ this function was called.
+ *
+ * Also, we avoid changing any global state (such as the current working
+ * directory) to allow early callers.
+ *
+ * The directory where the search should start needs to be passed in via the
+ * `dir` parameter; upon return, the `dir` buffer will contain the path of
+ * the directory where the search ended, and `gitdir` will contain the path of
+ * the discovered .git/ directory, if any. If `gitdir` is not absolute, it
+ * is relative to `dir` (i.e. *not* necessarily the cwd).
  */
-static const char *setup_git_directory_gently_1(int *nongit_ok)
+static enum discovery_result setup_git_directory_gently_1(struct strbuf *dir,
+                                                         struct strbuf *gitdir,
+                                                         int die_on_error)
 {
        const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
        struct string_list ceiling_dirs = STRING_LIST_INIT_DUP;
-       static struct strbuf cwd = STRBUF_INIT;
-       const char *gitdirenv, *ret;
-       char *gitfile;
-       int offset, offset_parent, ceil_offset = -1;
+       const char *gitdirenv;
+       int ceil_offset = -1, min_offset = has_dos_drive_prefix(dir->buf) ? 3 : 1;
        dev_t current_device = 0;
        int one_filesystem = 1;
 
-       /*
-        * We may have read an incomplete configuration before
-        * setting-up the git directory. If so, clear the cache so
-        * that the next queries to the configuration reload complete
-        * configuration (including the per-repo config file that we
-        * ignored previously).
-        */
-       git_config_clear();
-
-       /*
-        * Let's assume that we are in a git repository.
-        * If it turns out later that we are somewhere else, the value will be
-        * updated accordingly.
-        */
-       if (nongit_ok)
-               *nongit_ok = 0;
-
-       if (strbuf_getcwd(&cwd))
-               die_errno(_("Unable to read current working directory"));
-       offset = cwd.len;
-
        /*
         * If GIT_DIR is set explicitly, we're not going
         * to do any discovery, but we still do repository
         * validation.
         */
        gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
-       if (gitdirenv)
-               return setup_explicit_git_dir(gitdirenv, &cwd, nongit_ok);
+       if (gitdirenv) {
+               strbuf_addstr(gitdir, gitdirenv);
+               return GIT_DIR_EXPLICIT;
+       }
 
        if (env_ceiling_dirs) {
                int empty_entry_found = 0;
@@ -867,15 +876,15 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
                string_list_split(&ceiling_dirs, env_ceiling_dirs, PATH_SEP, -1);
                filter_string_list(&ceiling_dirs, 0,
                                   canonicalize_ceiling_entry, &empty_entry_found);
-               ceil_offset = longest_ancestor_length(cwd.buf, &ceiling_dirs);
+               ceil_offset = longest_ancestor_length(dir->buf, &ceiling_dirs);
                string_list_clear(&ceiling_dirs, 0);
        }
 
-       if (ceil_offset < 0 && has_dos_drive_prefix(cwd.buf))
-               ceil_offset = 1;
+       if (ceil_offset < 0)
+               ceil_offset = min_offset - 2;
 
        /*
-        * Test in the following order (relative to the cwd):
+        * Test in the following order (relative to the dir):
         * - .git (file containing "gitdir: <path>")
         * - .git/
         * - ./ (bare)
@@ -887,61 +896,159 @@ static const char *setup_git_directory_gently_1(int *nongit_ok)
         */
        one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
        if (one_filesystem)
-               current_device = get_device_or_die(".", NULL, 0);
+               current_device = get_device_or_die(dir->buf, NULL, 0);
        for (;;) {
-               gitfile = (char*)read_gitfile(DEFAULT_GIT_DIR_ENVIRONMENT);
-               if (gitfile)
-                       gitdirenv = gitfile = xstrdup(gitfile);
-               else {
-                       if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
-                               gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
+               int offset = dir->len, error_code = 0;
+
+               if (offset > min_offset)
+                       strbuf_addch(dir, '/');
+               strbuf_addstr(dir, DEFAULT_GIT_DIR_ENVIRONMENT);
+               gitdirenv = read_gitfile_gently(dir->buf, die_on_error ?
+                                               NULL : &error_code);
+               if (!gitdirenv) {
+                       if (die_on_error ||
+                           error_code == READ_GITFILE_ERR_NOT_A_FILE) {
+                               /* NEEDSWORK: fail if .git is not file nor dir */
+                               if (is_git_directory(dir->buf))
+                                       gitdirenv = DEFAULT_GIT_DIR_ENVIRONMENT;
+                       } else if (error_code != READ_GITFILE_ERR_STAT_FAILED)
+                               return GIT_DIR_INVALID_GITFILE;
                }
-
+               strbuf_setlen(dir, offset);
                if (gitdirenv) {
-                       ret = setup_discovered_git_dir(gitdirenv,
-                                                      &cwd, offset,
-                                                      nongit_ok);
-                       free(gitfile);
-                       return ret;
+                       strbuf_addstr(gitdir, gitdirenv);
+                       return GIT_DIR_DISCOVERED;
                }
-               free(gitfile);
 
-               if (is_git_directory("."))
-                       return setup_bare_git_dir(&cwd, offset, nongit_ok);
-
-               offset_parent = offset;
-               while (--offset_parent > ceil_offset && cwd.buf[offset_parent] != '/');
-               if (offset_parent <= ceil_offset)
-                       return setup_nongit(cwd.buf, nongit_ok);
-               if (one_filesystem) {
-                       dev_t parent_device = get_device_or_die("..", cwd.buf,
-                                                               offset);
-                       if (parent_device != current_device) {
-                               if (nongit_ok) {
-                                       if (chdir(cwd.buf))
-                                               die_errno(_("Cannot come back to cwd"));
-                                       *nongit_ok = 1;
-                                       return NULL;
-                               }
-                               strbuf_setlen(&cwd, offset);
-                               die(_("Not a git repository (or any parent up to mount point %s)\n"
-                               "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
-                                   cwd.buf);
-                       }
-               }
-               if (chdir("..")) {
-                       strbuf_setlen(&cwd, offset);
-                       die_errno(_("Cannot change to '%s/..'"), cwd.buf);
+               if (is_git_directory(dir->buf)) {
+                       strbuf_addstr(gitdir, ".");
+                       return GIT_DIR_BARE;
                }
-               offset = offset_parent;
+
+               if (offset <= min_offset)
+                       return GIT_DIR_HIT_CEILING;
+
+               while (--offset > ceil_offset && !is_dir_sep(dir->buf[offset]))
+                       ; /* continue */
+               if (offset <= ceil_offset)
+                       return GIT_DIR_HIT_CEILING;
+
+               strbuf_setlen(dir, offset > min_offset ?  offset : min_offset);
+               if (one_filesystem &&
+                   current_device != get_device_or_die(dir->buf, NULL, offset))
+                       return GIT_DIR_HIT_MOUNT_POINT;
+       }
+}
+
+const char *discover_git_directory(struct strbuf *gitdir)
+{
+       struct strbuf dir = STRBUF_INIT, err = STRBUF_INIT;
+       size_t gitdir_offset = gitdir->len, cwd_len;
+       struct repository_format candidate;
+
+       if (strbuf_getcwd(&dir))
+               return NULL;
+
+       cwd_len = dir.len;
+       if (setup_git_directory_gently_1(&dir, gitdir, 0) <= 0) {
+               strbuf_release(&dir);
+               return NULL;
        }
+
+       /*
+        * The returned gitdir is relative to dir, and if dir does not reflect
+        * the current working directory, we simply make the gitdir absolute.
+        */
+       if (dir.len < cwd_len && !is_absolute_path(gitdir->buf + gitdir_offset)) {
+               /* Avoid a trailing "/." */
+               if (!strcmp(".", gitdir->buf + gitdir_offset))
+                       strbuf_setlen(gitdir, gitdir_offset);
+               else
+                       strbuf_addch(&dir, '/');
+               strbuf_insert(gitdir, gitdir_offset, dir.buf, dir.len);
+       }
+
+       strbuf_reset(&dir);
+       strbuf_addf(&dir, "%s/config", gitdir->buf + gitdir_offset);
+       read_repository_format(&candidate, dir.buf);
+       strbuf_release(&dir);
+
+       if (verify_repository_format(&candidate, &err) < 0) {
+               warning("ignoring git dir '%s': %s",
+                       gitdir->buf + gitdir_offset, err.buf);
+               strbuf_release(&err);
+               return NULL;
+       }
+
+       return gitdir->buf + gitdir_offset;
 }
 
 const char *setup_git_directory_gently(int *nongit_ok)
 {
-       const char *prefix;
+       static struct strbuf cwd = STRBUF_INIT;
+       struct strbuf dir = STRBUF_INIT, gitdir = STRBUF_INIT;
+       const char *prefix, *env_prefix;
+
+       /*
+        * We may have read an incomplete configuration before
+        * setting-up the git directory. If so, clear the cache so
+        * that the next queries to the configuration reload complete
+        * configuration (including the per-repo config file that we
+        * ignored previously).
+        */
+       git_config_clear();
+
+       /*
+        * Let's assume that we are in a git repository.
+        * If it turns out later that we are somewhere else, the value will be
+        * updated accordingly.
+        */
+       if (nongit_ok)
+               *nongit_ok = 0;
+
+       if (strbuf_getcwd(&cwd))
+               die_errno(_("Unable to read current working directory"));
+       strbuf_addbuf(&dir, &cwd);
+
+       switch (setup_git_directory_gently_1(&dir, &gitdir, 1)) {
+       case GIT_DIR_NONE:
+               prefix = NULL;
+               break;
+       case GIT_DIR_EXPLICIT:
+               prefix = setup_explicit_git_dir(gitdir.buf, &cwd, nongit_ok);
+               break;
+       case GIT_DIR_DISCOVERED:
+               if (dir.len < cwd.len && chdir(dir.buf))
+                       die(_("Cannot change to '%s'"), dir.buf);
+               prefix = setup_discovered_git_dir(gitdir.buf, &cwd, dir.len,
+                                                 nongit_ok);
+               break;
+       case GIT_DIR_BARE:
+               if (dir.len < cwd.len && chdir(dir.buf))
+                       die(_("Cannot change to '%s'"), dir.buf);
+               prefix = setup_bare_git_dir(&cwd, dir.len, nongit_ok);
+               break;
+       case GIT_DIR_HIT_CEILING:
+               prefix = setup_nongit(cwd.buf, nongit_ok);
+               break;
+       case GIT_DIR_HIT_MOUNT_POINT:
+               if (nongit_ok) {
+                       *nongit_ok = 1;
+                       strbuf_release(&cwd);
+                       strbuf_release(&dir);
+                       return NULL;
+               }
+               die(_("Not a git repository (or any parent up to mount point %s)\n"
+                     "Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set)."),
+                   dir.buf);
+       default:
+               die("BUG: unhandled setup_git_directory_1() result");
+       }
+
+       env_prefix = getenv(GIT_TOPLEVEL_PREFIX_ENVIRONMENT);
+       if (env_prefix)
+               prefix = env_prefix;
 
-       prefix = setup_git_directory_gently_1(nongit_ok);
        if (prefix)
                setenv(GIT_PREFIX_ENVIRONMENT, prefix, 1);
        else
@@ -950,6 +1057,9 @@ const char *setup_git_directory_gently(int *nongit_ok)
        startup_info->have_repository = !nongit_ok || !*nongit_ok;
        startup_info->prefix = prefix;
 
+       strbuf_release(&dir);
+       strbuf_release(&gitdir);
+
        return prefix;
 }
 
index c1cc25cd95da66aec1c695f4bba9680f5cb834f6..7d646ab5b8cf80e47c6c1f339affeaa76a41d2d2 100644 (file)
@@ -2,60 +2,60 @@
 #include "sha1-array.h"
 #include "sha1-lookup.h"
 
-void sha1_array_append(struct sha1_array *array, const unsigned char *sha1)
+void oid_array_append(struct oid_array *array, const struct object_id *oid)
 {
-       ALLOC_GROW(array->sha1, array->nr + 1, array->alloc);
-       hashcpy(array->sha1[array->nr++], sha1);
+       ALLOC_GROW(array->oid, array->nr + 1, array->alloc);
+       oidcpy(&array->oid[array->nr++], oid);
        array->sorted = 0;
 }
 
 static int void_hashcmp(const void *a, const void *b)
 {
-       return hashcmp(a, b);
+       return oidcmp(a, b);
 }
 
-static void sha1_array_sort(struct sha1_array *array)
+static void oid_array_sort(struct oid_array *array)
 {
-       QSORT(array->sha1, array->nr, void_hashcmp);
+       QSORT(array->oid, array->nr, void_hashcmp);
        array->sorted = 1;
 }
 
 static const unsigned char *sha1_access(size_t index, void *table)
 {
-       unsigned char (*array)[20] = table;
-       return array[index];
+       struct object_id *array = table;
+       return array[index].hash;
 }
 
-int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1)
+int oid_array_lookup(struct oid_array *array, const struct object_id *oid)
 {
        if (!array->sorted)
-               sha1_array_sort(array);
-       return sha1_pos(sha1, array->sha1, array->nr, sha1_access);
+               oid_array_sort(array);
+       return sha1_pos(oid->hash, array->oid, array->nr, sha1_access);
 }
 
-void sha1_array_clear(struct sha1_array *array)
+void oid_array_clear(struct oid_array *array)
 {
-       free(array->sha1);
-       array->sha1 = NULL;
+       free(array->oid);
+       array->oid = NULL;
        array->nr = 0;
        array->alloc = 0;
        array->sorted = 0;
 }
 
-int sha1_array_for_each_unique(struct sha1_array *array,
-                               for_each_sha1_fn fn,
+int oid_array_for_each_unique(struct oid_array *array,
+                               for_each_oid_fn fn,
                                void *data)
 {
        int i;
 
        if (!array->sorted)
-               sha1_array_sort(array);
+               oid_array_sort(array);
 
        for (i = 0; i < array->nr; i++) {
                int ret;
-               if (i > 0 && !hashcmp(array->sha1[i], array->sha1[i-1]))
+               if (i > 0 && !oidcmp(array->oid + i, array->oid + i - 1))
                        continue;
-               ret = fn(array->sha1[i], data);
+               ret = fn(array->oid + i, data);
                if (ret)
                        return ret;
        }
index b3230be0dd6eedf871c5c337a79333a7ebf33cb7..04b0756334da7adf1e3eefb3f521f8c95d263046 100644 (file)
@@ -1,23 +1,23 @@
 #ifndef SHA1_ARRAY_H
 #define SHA1_ARRAY_H
 
-struct sha1_array {
-       unsigned char (*sha1)[20];
+struct oid_array {
+       struct object_id *oid;
        int nr;
        int alloc;
        int sorted;
 };
 
-#define SHA1_ARRAY_INIT { NULL, 0, 0, 0 }
+#define OID_ARRAY_INIT { NULL, 0, 0, 0 }
 
-void sha1_array_append(struct sha1_array *array, const unsigned char *sha1);
-int sha1_array_lookup(struct sha1_array *array, const unsigned char *sha1);
-void sha1_array_clear(struct sha1_array *array);
+void oid_array_append(struct oid_array *array, const struct object_id *oid);
+int oid_array_lookup(struct oid_array *array, const struct object_id *oid);
+void oid_array_clear(struct oid_array *array);
 
-typedef int (*for_each_sha1_fn)(const unsigned char sha1[20],
-                               void *data);
-int sha1_array_for_each_unique(struct sha1_array *array,
-                              for_each_sha1_fn fn,
+typedef int (*for_each_oid_fn)(const struct object_id *oid,
+                              void *data);
+int oid_array_for_each_unique(struct oid_array *array,
+                              for_each_oid_fn fn,
                               void *data);
 
 #endif /* SHA1_ARRAY_H */
index 29bbc5f427c9fe390f15876775fa7e27bc15e4fb..59a4ed2ed32336b41ab8f3b1d4aca30f045aa084 100644 (file)
@@ -277,31 +277,26 @@ static const char *alt_sha1_path(struct alternate_object_database *alt,
        return buf->buf;
 }
 
-/*
- * Return the name of the pack or index file with the specified sha1
- * in its filename.  *base and *name are scratch space that must be
- * provided by the caller.  which should be "pack" or "idx".
- */
-static char *sha1_get_pack_name(const unsigned char *sha1,
-                               struct strbuf *buf,
-                               const char *which)
+ char *odb_pack_name(struct strbuf *buf,
+                    const unsigned char *sha1,
+                    const char *ext)
 {
        strbuf_reset(buf);
        strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
-                   sha1_to_hex(sha1), which);
+                   sha1_to_hex(sha1), ext);
        return buf->buf;
 }
 
 char *sha1_pack_name(const unsigned char *sha1)
 {
        static struct strbuf buf = STRBUF_INIT;
-       return sha1_get_pack_name(sha1, &buf, "pack");
+       return odb_pack_name(&buf, sha1, "pack");
 }
 
 char *sha1_pack_index_name(const unsigned char *sha1)
 {
        static struct strbuf buf = STRBUF_INIT;
-       return sha1_get_pack_name(sha1, &buf, "idx");
+       return odb_pack_name(&buf, sha1, "idx");
 }
 
 struct alternate_object_database *alt_odb_list;
@@ -1611,7 +1606,7 @@ static void mark_bad_packed_object(struct packed_git *p,
                if (!hashcmp(sha1, p->bad_object_sha1 + GIT_SHA1_RAWSZ * i))
                        return;
        p->bad_object_sha1 = xrealloc(p->bad_object_sha1,
-                                     st_mult(GIT_SHA1_RAWSZ,
+                                     st_mult(GIT_MAX_RAWSZ,
                                              st_add(p->num_bad_objects, 1)));
        hashcpy(p->bad_object_sha1 + GIT_SHA1_RAWSZ * p->num_bad_objects, sha1);
        p->num_bad_objects++;
@@ -2957,7 +2952,7 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        if (status && oi->typep)
                *oi->typep = status;
        strbuf_release(&hdrbuf);
-       return 0;
+       return (status < 0) ? status : 0;
 }
 
 int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi, unsigned flags)
@@ -3486,6 +3481,8 @@ int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
 {
        struct pack_entry e;
 
+       if (!startup_info->have_repository)
+               return 0;
        if (find_pack_entry(sha1, &e))
                return 1;
        if (has_loose_object(sha1))
@@ -3764,11 +3761,11 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
                strbuf_addf(path, "/%s", de->d_name);
 
                if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
-                       char hex[GIT_SHA1_HEXSZ+1];
+                       char hex[GIT_MAX_HEXSZ+1];
                        struct object_id oid;
 
-                       snprintf(hex, sizeof(hex), "%02x%s",
-                                subdir_nr, de->d_name);
+                       xsnprintf(hex, sizeof(hex), "%02x%s",
+                                 subdir_nr, de->d_name);
                        if (!get_oid_hex(hex, &oid)) {
                                if (obj_cb) {
                                        r = obj_cb(&oid, path->buf, data);
@@ -3918,7 +3915,7 @@ static int check_stream_sha1(git_zstream *stream,
                             const unsigned char *expected_sha1)
 {
        git_SHA_CTX c;
-       unsigned char real_sha1[GIT_SHA1_RAWSZ];
+       unsigned char real_sha1[GIT_MAX_RAWSZ];
        unsigned char buf[4096];
        unsigned long total_read;
        int status = Z_OK;
@@ -3975,7 +3972,6 @@ int read_loose_object(const char *path,
                      void **contents)
 {
        int ret = -1;
-       int fd = -1;
        void *map = NULL;
        unsigned long mapsize;
        git_zstream stream;
@@ -4025,7 +4021,5 @@ int read_loose_object(const char *path,
 out:
        if (map)
                munmap(map, mapsize);
-       if (fd >= 0)
-               close(fd);
        return ret;
 }
index cda9e49b12e7128dd22a8ccbaff437cb7db34d92..8eec9f7c1bb59b8124e098367b7bff552bcafcf4 100644 (file)
 
 static int get_sha1_oneline(const char *, unsigned char *, struct commit_list *);
 
-typedef int (*disambiguate_hint_fn)(const unsigned char *, void *);
+typedef int (*disambiguate_hint_fn)(const struct object_id *, void *);
 
 struct disambiguate_state {
        int len; /* length of prefix in hex chars */
-       char hex_pfx[GIT_SHA1_HEXSZ + 1];
-       unsigned char bin_pfx[GIT_SHA1_RAWSZ];
+       char hex_pfx[GIT_MAX_HEXSZ + 1];
+       struct object_id bin_pfx;
 
        disambiguate_hint_fn fn;
        void *cb_data;
-       unsigned char candidate[GIT_SHA1_RAWSZ];
+       struct object_id candidate;
        unsigned candidate_exists:1;
        unsigned candidate_checked:1;
        unsigned candidate_ok:1;
@@ -29,7 +29,7 @@ struct disambiguate_state {
        unsigned always_call_fn:1;
 };
 
-static void update_candidates(struct disambiguate_state *ds, const unsigned char *current)
+static void update_candidates(struct disambiguate_state *ds, const struct object_id *current)
 {
        if (ds->always_call_fn) {
                ds->ambiguous = ds->fn(current, ds->cb_data) ? 1 : 0;
@@ -37,10 +37,10 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
        }
        if (!ds->candidate_exists) {
                /* this is the first candidate */
-               hashcpy(ds->candidate, current);
+               oidcpy(&ds->candidate, current);
                ds->candidate_exists = 1;
                return;
-       } else if (!hashcmp(ds->candidate, current)) {
+       } else if (!oidcmp(&ds->candidate, current)) {
                /* the same as what we already have seen */
                return;
        }
@@ -52,14 +52,14 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
        }
 
        if (!ds->candidate_checked) {
-               ds->candidate_ok = ds->fn(ds->candidate, ds->cb_data);
+               ds->candidate_ok = ds->fn(&ds->candidate, ds->cb_data);
                ds->disambiguate_fn_used = 1;
                ds->candidate_checked = 1;
        }
 
        if (!ds->candidate_ok) {
                /* discard the candidate; we know it does not satisfy fn */
-               hashcpy(ds->candidate, current);
+               oidcpy(&ds->candidate, current);
                ds->candidate_checked = 0;
                return;
        }
@@ -80,7 +80,7 @@ static void update_candidates(struct disambiguate_state *ds, const unsigned char
 static void find_short_object_filename(struct disambiguate_state *ds)
 {
        struct alternate_object_database *alt;
-       char hex[GIT_SHA1_HEXSZ];
+       char hex[GIT_MAX_HEXSZ];
        static struct alternate_object_database *fakeent;
 
        if (!fakeent) {
@@ -107,15 +107,15 @@ static void find_short_object_filename(struct disambiguate_state *ds)
                        continue;
 
                while (!ds->ambiguous && (de = readdir(dir)) != NULL) {
-                       unsigned char sha1[20];
+                       struct object_id oid;
 
-                       if (strlen(de->d_name) != 38)
+                       if (strlen(de->d_name) != GIT_SHA1_HEXSZ - 2)
                                continue;
                        if (memcmp(de->d_name, ds->hex_pfx + 2, ds->len - 2))
                                continue;
-                       memcpy(hex + 2, de->d_name, 38);
-                       if (!get_sha1_hex(hex, sha1))
-                               update_candidates(ds, sha1);
+                       memcpy(hex + 2, de->d_name, GIT_SHA1_HEXSZ - 2);
+                       if (!get_oid_hex(hex, &oid))
+                               update_candidates(ds, &oid);
                }
                closedir(dir);
        }
@@ -140,7 +140,7 @@ static void unique_in_pack(struct packed_git *p,
                           struct disambiguate_state *ds)
 {
        uint32_t num, last, i, first = 0;
-       const unsigned char *current = NULL;
+       const struct object_id *current = NULL;
 
        open_pack_index(p);
        num = p->num_objects;
@@ -151,7 +151,7 @@ static void unique_in_pack(struct packed_git *p,
                int cmp;
 
                current = nth_packed_object_sha1(p, mid);
-               cmp = hashcmp(ds->bin_pfx, current);
+               cmp = hashcmp(ds->bin_pfx.hash, current);
                if (!cmp) {
                        first = mid;
                        break;
@@ -169,8 +169,9 @@ static void unique_in_pack(struct packed_git *p,
         * 0, 1 or more objects that actually match(es).
         */
        for (i = first; i < num && !ds->ambiguous; i++) {
-               current = nth_packed_object_sha1(p, i);
-               if (!match_sha(ds->len, ds->bin_pfx, current))
+               struct object_id oid;
+               current = nth_packed_object_oid(&oid, p, i);
+               if (!match_sha(ds->len, ds->bin_pfx.hash, current->hash))
                        break;
                update_candidates(ds, current);
        }
@@ -213,66 +214,66 @@ static int finish_object_disambiguation(struct disambiguate_state *ds,
                 * same repository!
                 */
                ds->candidate_ok = (!ds->disambiguate_fn_used ||
-                                   ds->fn(ds->candidate, ds->cb_data));
+                                   ds->fn(&ds->candidate, ds->cb_data));
 
        if (!ds->candidate_ok)
                return SHORT_NAME_AMBIGUOUS;
 
-       hashcpy(sha1, ds->candidate);
+       hashcpy(sha1, ds->candidate.hash);
        return 0;
 }
 
-static int disambiguate_commit_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_commit_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_COMMIT;
 }
 
-static int disambiguate_committish_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_committish_only(const struct object_id *oid, void *cb_data_unused)
 {
        struct object *obj;
        int kind;
 
-       kind = sha1_object_info(sha1, NULL);
+       kind = sha1_object_info(oid->hash, NULL);
        if (kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(parse_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(oid->hash), NULL, 0);
        if (obj && obj->type == OBJ_COMMIT)
                return 1;
        return 0;
 }
 
-static int disambiguate_tree_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_tree_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_TREE;
 }
 
-static int disambiguate_treeish_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_treeish_only(const struct object_id *oid, void *cb_data_unused)
 {
        struct object *obj;
        int kind;
 
-       kind = sha1_object_info(sha1, NULL);
+       kind = sha1_object_info(oid->hash, NULL);
        if (kind == OBJ_TREE || kind == OBJ_COMMIT)
                return 1;
        if (kind != OBJ_TAG)
                return 0;
 
        /* We need to do this the hard way... */
-       obj = deref_tag(parse_object(sha1), NULL, 0);
+       obj = deref_tag(parse_object(oid->hash), NULL, 0);
        if (obj && (obj->type == OBJ_TREE || obj->type == OBJ_COMMIT))
                return 1;
        return 0;
 }
 
-static int disambiguate_blob_only(const unsigned char *sha1, void *cb_data_unused)
+static int disambiguate_blob_only(const struct object_id *oid, void *cb_data_unused)
 {
-       int kind = sha1_object_info(sha1, NULL);
+       int kind = sha1_object_info(oid->hash, NULL);
        return kind == OBJ_BLOB;
 }
 
@@ -332,7 +333,7 @@ static int init_object_disambiguation(const char *name, int len,
                ds->hex_pfx[i] = c;
                if (!(i & 1))
                        val <<= 4;
-               ds->bin_pfx[i >> 1] |= val;
+               ds->bin_pfx.hash[i >> 1] |= val;
        }
 
        ds->len = len;
@@ -341,31 +342,32 @@ static int init_object_disambiguation(const char *name, int len,
        return 0;
 }
 
-static int show_ambiguous_object(const unsigned char *sha1, void *data)
+static int show_ambiguous_object(const struct object_id *oid, void *data)
 {
        const struct disambiguate_state *ds = data;
        struct strbuf desc = STRBUF_INIT;
        int type;
 
-       if (ds->fn && !ds->fn(sha1, ds->cb_data))
+
+       if (ds->fn && !ds->fn(oid, ds->cb_data))
                return 0;
 
-       type = sha1_object_info(sha1, NULL);
+       type = sha1_object_info(oid->hash, NULL);
        if (type == OBJ_COMMIT) {
-               struct commit *commit = lookup_commit(sha1);
+               struct commit *commit = lookup_commit(oid->hash);
                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(sha1);
+               struct tag *tag = lookup_tag(oid->hash);
                if (!parse_tag(tag) && tag->tag)
                        strbuf_addf(&desc, " %s", tag->tag);
        }
 
        advise("  %s %s%s",
-              find_unique_abbrev(sha1, DEFAULT_ABBREV),
+              find_unique_abbrev(oid->hash, DEFAULT_ABBREV),
               typename(type) ? typename(type) : "unknown type",
               desc.buf);
 
@@ -422,15 +424,15 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
        return status;
 }
 
-static int collect_ambiguous(const unsigned char *sha1, void *data)
+static int collect_ambiguous(const struct object_id *oid, void *data)
 {
-       sha1_array_append(data, sha1);
+       oid_array_append(data, oid);
        return 0;
 }
 
 int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
 {
-       struct sha1_array collect = SHA1_ARRAY_INIT;
+       struct oid_array collect = OID_ARRAY_INIT;
        struct disambiguate_state ds;
        int ret;
 
@@ -443,8 +445,8 @@ int for_each_abbrev(const char *prefix, each_abbrev_fn fn, void *cb_data)
        find_short_object_filename(&ds);
        find_short_packed_object(&ds);
 
-       ret = sha1_array_for_each_unique(&collect, fn, cb_data);
-       sha1_array_clear(&collect);
+       ret = oid_array_for_each_unique(&collect, fn, cb_data);
+       oid_array_clear(&collect);
        return ret;
 }
 
@@ -509,7 +511,7 @@ int find_unique_abbrev_r(char *hex, const unsigned char *sha1, int len)
 const char *find_unique_abbrev(const unsigned char *sha1, int len)
 {
        static int bufno;
-       static char hexbuffer[4][GIT_SHA1_HEXSZ + 1];
+       static char hexbuffer[4][GIT_MAX_HEXSZ + 1];
        char *hex = hexbuffer[bufno];
        bufno = (bufno + 1) % ARRAY_SIZE(hexbuffer);
        find_unique_abbrev_r(hex, sha1, len);
@@ -549,7 +551,7 @@ static inline int at_mark(const char *string, int len,
        for (i = 0; i < nr; i++) {
                int suffix_len = strlen(suffix[i]);
                if (suffix_len <= len
-                   && !memcmp(string, suffix[i], suffix_len))
+                   && !strncasecmp(string, suffix[i], suffix_len))
                        return suffix_len;
        }
        return 0;
diff --git a/sha1dc/LICENSE.txt b/sha1dc/LICENSE.txt
new file mode 100644 (file)
index 0000000..4a3e6a1
--- /dev/null
@@ -0,0 +1,30 @@
+MIT License
+
+Copyright (c) 2017:
+    Marc Stevens
+    Cryptology Group
+    Centrum Wiskunde & Informatica
+    P.O. Box 94079, 1090 GB Amsterdam, Netherlands
+    marc@marc-stevens.nl
+
+    Dan Shumow
+    Microsoft Research
+    danshu@microsoft.com
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/sha1dc/sha1.c b/sha1dc/sha1.c
new file mode 100644 (file)
index 0000000..35e9dd5
--- /dev/null
@@ -0,0 +1,1809 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow (danshu@microsoft.com)
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+#include "cache.h"
+#include "sha1dc/sha1.h"
+#include "sha1dc/ubc_check.h"
+
+
+/*
+   Because Little-Endian architectures are most common,
+   we only set SHA1DC_BIGENDIAN if one of these conditions is met.
+   Note that all MSFT platforms are little endian,
+   so none of these will be defined under the MSC compiler.
+   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__)
+
+#define SHA1DC_BIGENDIAN       1
+#else
+#undef SHA1DC_BIGENDIAN
+#endif /*ENDIANNESS SELECTION*/
+
+#define rotate_right(x,n) (((x)>>(n))|((x)<<(32-(n))))
+#define rotate_left(x,n)  (((x)<<(n))|((x)>>(32-(n))))
+
+#define sha1_bswap32(x) \
+       {x = ((x << 8) & 0xFF00FF00) | ((x >> 8) & 0xFF00FF); x = (x << 16) | (x >> 16);}
+
+#define sha1_mix(W, t)  (rotate_left(W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16], 1))
+
+#if defined(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) */
+
+#define sha1_store(W, t, x)    *(volatile uint32_t *)&W[t] = x
+
+#define sha1_f1(b,c,d) ((d)^((b)&((c)^(d))))
+#define sha1_f2(b,c,d) ((b)^(c)^(d))
+#define sha1_f3(b,c,d) (((b)&(c))+((d)&((b)^(c))))
+#define sha1_f4(b,c,d) ((b)^(c)^(d))
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, m, t) \
+       { e += rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, m, t) \
+       { e += rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, m, t) \
+       { e += rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; b = rotate_left(b, 30); }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, m, t) \
+       { e += rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; b = rotate_left(b, 30); }
+
+#define HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, m, t) \
+       { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999 + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, m, t) \
+       { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1 + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, m, t) \
+       { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC + m[t]; }
+#define HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, m, t) \
+       { b = rotate_right(b, 30); e -= rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6 + m[t]; }
+
+#define SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, t, temp) \
+       {sha1_load(m, t, temp); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30);}
+
+#define SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(a, b, c, d, e, W, t, temp) \
+       {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f1(b,c,d) + 0x5A827999; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, t, temp) \
+       {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f2(b,c,d) + 0x6ED9EBA1; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, t, temp) \
+       {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f3(b,c,d) + 0x8F1BBCDC; b = rotate_left(b, 30); }
+
+#define SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, t, temp) \
+       {temp = sha1_mix(W, t); sha1_store(W, t, temp); e += temp + rotate_left(a, 5) + sha1_f4(b,c,d) + 0xCA62C1D6; b = rotate_left(b, 30); }
+
+
+#define SHA1_STORE_STATE(i) states[i][0] = a; states[i][1] = b; states[i][2] = c; states[i][3] = d; states[i][4] = e;
+
+#ifdef BUILDNOCOLLDETECTSHA1COMPRESSION
+void sha1_compression(uint32_t ihv[5], const uint32_t m[16])
+{
+       uint32_t W[80];
+       uint32_t a,b,c,d,e;
+       unsigned i;
+
+       memcpy(W, m, 16 * 4);
+       for (i = 16; i < 80; ++i)
+               W[i] = sha1_mix(W, i);
+
+       a = ihv[0]; b = ihv[1]; c = ihv[2]; d = ihv[3]; e = ihv[4];
+
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+       ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+#endif /*BUILDNOCOLLDETECTSHA1COMPRESSION*/
+
+
+static void sha1_compression_W(uint32_t ihv[5], const uint32_t W[80])
+{
+       uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 0);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 1);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 2);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 3);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 4);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 5);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 6);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 7);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 8);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 9);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 10);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 11);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 12);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 13);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 14);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, W, 15);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, W, 16);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, W, 17);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, W, 18);
+       HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, W, 19);
+
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 20);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 21);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 22);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 23);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 24);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 25);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 26);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 27);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 28);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 29);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 30);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 31);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 32);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 33);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 34);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, W, 35);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, W, 36);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, W, 37);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, W, 38);
+       HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, W, 39);
+
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 40);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 41);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 42);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 43);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 44);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 45);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 46);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 47);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 48);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 49);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 50);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 51);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 52);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 53);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 54);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, W, 55);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, W, 56);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, W, 57);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, W, 58);
+       HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, W, 59);
+
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 60);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 61);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 62);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 63);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 64);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 65);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 66);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 67);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 68);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 69);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 70);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 71);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 72);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 73);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 74);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, W, 75);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, W, 76);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, W, 77);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, W, 78);
+       HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, W, 79);
+
+       ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+void sha1_compression_states(uint32_t ihv[5], const uint32_t m[16], uint32_t W[80], uint32_t states[80][5])
+{
+       uint32_t a = ihv[0], b = ihv[1], c = ihv[2], d = ihv[3], e = ihv[4];
+       uint32_t temp;
+
+#ifdef DOSTORESTATE00
+       SHA1_STORE_STATE(0)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 0, temp);
+
+#ifdef DOSTORESTATE01
+       SHA1_STORE_STATE(1)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 1, temp);
+
+#ifdef DOSTORESTATE02
+       SHA1_STORE_STATE(2)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 2, temp);
+
+#ifdef DOSTORESTATE03
+       SHA1_STORE_STATE(3)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 3, temp);
+
+#ifdef DOSTORESTATE04
+       SHA1_STORE_STATE(4)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 4, temp);
+
+#ifdef DOSTORESTATE05
+       SHA1_STORE_STATE(5)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 5, temp);
+
+#ifdef DOSTORESTATE06
+       SHA1_STORE_STATE(6)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 6, temp);
+
+#ifdef DOSTORESTATE07
+       SHA1_STORE_STATE(7)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 7, temp);
+
+#ifdef DOSTORESTATE08
+       SHA1_STORE_STATE(8)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 8, temp);
+
+#ifdef DOSTORESTATE09
+       SHA1_STORE_STATE(9)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 9, temp);
+
+#ifdef DOSTORESTATE10
+       SHA1_STORE_STATE(10)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 10, temp);
+
+#ifdef DOSTORESTATE11
+       SHA1_STORE_STATE(11)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(e, a, b, c, d, m, W, 11, temp);
+
+#ifdef DOSTORESTATE12
+       SHA1_STORE_STATE(12)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(d, e, a, b, c, m, W, 12, temp);
+
+#ifdef DOSTORESTATE13
+       SHA1_STORE_STATE(13)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(c, d, e, a, b, m, W, 13, temp);
+
+#ifdef DOSTORESTATE14
+       SHA1_STORE_STATE(14)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(b, c, d, e, a, m, W, 14, temp);
+
+#ifdef DOSTORESTATE15
+       SHA1_STORE_STATE(15)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_LOAD(a, b, c, d, e, m, W, 15, temp);
+
+#ifdef DOSTORESTATE16
+       SHA1_STORE_STATE(16)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(e, a, b, c, d, W, 16, temp);
+
+#ifdef DOSTORESTATE17
+       SHA1_STORE_STATE(17)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(d, e, a, b, c, W, 17, temp);
+
+#ifdef DOSTORESTATE18
+       SHA1_STORE_STATE(18)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(c, d, e, a, b, W, 18, temp);
+
+#ifdef DOSTORESTATE19
+       SHA1_STORE_STATE(19)
+#endif
+       SHA1COMPRESS_FULL_ROUND1_STEP_EXPAND(b, c, d, e, a, W, 19, temp);
+
+
+
+#ifdef DOSTORESTATE20
+       SHA1_STORE_STATE(20)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 20, temp);
+
+#ifdef DOSTORESTATE21
+       SHA1_STORE_STATE(21)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 21, temp);
+
+#ifdef DOSTORESTATE22
+       SHA1_STORE_STATE(22)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 22, temp);
+
+#ifdef DOSTORESTATE23
+       SHA1_STORE_STATE(23)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 23, temp);
+
+#ifdef DOSTORESTATE24
+       SHA1_STORE_STATE(24)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 24, temp);
+
+#ifdef DOSTORESTATE25
+       SHA1_STORE_STATE(25)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 25, temp);
+
+#ifdef DOSTORESTATE26
+       SHA1_STORE_STATE(26)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 26, temp);
+
+#ifdef DOSTORESTATE27
+       SHA1_STORE_STATE(27)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 27, temp);
+
+#ifdef DOSTORESTATE28
+       SHA1_STORE_STATE(28)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 28, temp);
+
+#ifdef DOSTORESTATE29
+       SHA1_STORE_STATE(29)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 29, temp);
+
+#ifdef DOSTORESTATE30
+       SHA1_STORE_STATE(30)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 30, temp);
+
+#ifdef DOSTORESTATE31
+       SHA1_STORE_STATE(31)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 31, temp);
+
+#ifdef DOSTORESTATE32
+       SHA1_STORE_STATE(32)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 32, temp);
+
+#ifdef DOSTORESTATE33
+       SHA1_STORE_STATE(33)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 33, temp);
+
+#ifdef DOSTORESTATE34
+       SHA1_STORE_STATE(34)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 34, temp);
+
+#ifdef DOSTORESTATE35
+       SHA1_STORE_STATE(35)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(a, b, c, d, e, W, 35, temp);
+
+#ifdef DOSTORESTATE36
+       SHA1_STORE_STATE(36)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(e, a, b, c, d, W, 36, temp);
+
+#ifdef DOSTORESTATE37
+       SHA1_STORE_STATE(37)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(d, e, a, b, c, W, 37, temp);
+
+#ifdef DOSTORESTATE38
+       SHA1_STORE_STATE(38)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(c, d, e, a, b, W, 38, temp);
+
+#ifdef DOSTORESTATE39
+       SHA1_STORE_STATE(39)
+#endif
+       SHA1COMPRESS_FULL_ROUND2_STEP(b, c, d, e, a, W, 39, temp);
+
+
+
+#ifdef DOSTORESTATE40
+       SHA1_STORE_STATE(40)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 40, temp);
+
+#ifdef DOSTORESTATE41
+       SHA1_STORE_STATE(41)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 41, temp);
+
+#ifdef DOSTORESTATE42
+       SHA1_STORE_STATE(42)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 42, temp);
+
+#ifdef DOSTORESTATE43
+       SHA1_STORE_STATE(43)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 43, temp);
+
+#ifdef DOSTORESTATE44
+       SHA1_STORE_STATE(44)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 44, temp);
+
+#ifdef DOSTORESTATE45
+       SHA1_STORE_STATE(45)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 45, temp);
+
+#ifdef DOSTORESTATE46
+       SHA1_STORE_STATE(46)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 46, temp);
+
+#ifdef DOSTORESTATE47
+       SHA1_STORE_STATE(47)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 47, temp);
+
+#ifdef DOSTORESTATE48
+       SHA1_STORE_STATE(48)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 48, temp);
+
+#ifdef DOSTORESTATE49
+       SHA1_STORE_STATE(49)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 49, temp);
+
+#ifdef DOSTORESTATE50
+       SHA1_STORE_STATE(50)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 50, temp);
+
+#ifdef DOSTORESTATE51
+       SHA1_STORE_STATE(51)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 51, temp);
+
+#ifdef DOSTORESTATE52
+       SHA1_STORE_STATE(52)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 52, temp);
+
+#ifdef DOSTORESTATE53
+       SHA1_STORE_STATE(53)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 53, temp);
+
+#ifdef DOSTORESTATE54
+       SHA1_STORE_STATE(54)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 54, temp);
+
+#ifdef DOSTORESTATE55
+       SHA1_STORE_STATE(55)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(a, b, c, d, e, W, 55, temp);
+
+#ifdef DOSTORESTATE56
+       SHA1_STORE_STATE(56)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(e, a, b, c, d, W, 56, temp);
+
+#ifdef DOSTORESTATE57
+       SHA1_STORE_STATE(57)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(d, e, a, b, c, W, 57, temp);
+
+#ifdef DOSTORESTATE58
+       SHA1_STORE_STATE(58)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(c, d, e, a, b, W, 58, temp);
+
+#ifdef DOSTORESTATE59
+       SHA1_STORE_STATE(59)
+#endif
+       SHA1COMPRESS_FULL_ROUND3_STEP(b, c, d, e, a, W, 59, temp);
+
+
+
+
+#ifdef DOSTORESTATE60
+       SHA1_STORE_STATE(60)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 60, temp);
+
+#ifdef DOSTORESTATE61
+       SHA1_STORE_STATE(61)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 61, temp);
+
+#ifdef DOSTORESTATE62
+       SHA1_STORE_STATE(62)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 62, temp);
+
+#ifdef DOSTORESTATE63
+       SHA1_STORE_STATE(63)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 63, temp);
+
+#ifdef DOSTORESTATE64
+       SHA1_STORE_STATE(64)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 64, temp);
+
+#ifdef DOSTORESTATE65
+       SHA1_STORE_STATE(65)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 65, temp);
+
+#ifdef DOSTORESTATE66
+       SHA1_STORE_STATE(66)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 66, temp);
+
+#ifdef DOSTORESTATE67
+       SHA1_STORE_STATE(67)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 67, temp);
+
+#ifdef DOSTORESTATE68
+       SHA1_STORE_STATE(68)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 68, temp);
+
+#ifdef DOSTORESTATE69
+       SHA1_STORE_STATE(69)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 69, temp);
+
+#ifdef DOSTORESTATE70
+       SHA1_STORE_STATE(70)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 70, temp);
+
+#ifdef DOSTORESTATE71
+       SHA1_STORE_STATE(71)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 71, temp);
+
+#ifdef DOSTORESTATE72
+       SHA1_STORE_STATE(72)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 72, temp);
+
+#ifdef DOSTORESTATE73
+       SHA1_STORE_STATE(73)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 73, temp);
+
+#ifdef DOSTORESTATE74
+       SHA1_STORE_STATE(74)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 74, temp);
+
+#ifdef DOSTORESTATE75
+       SHA1_STORE_STATE(75)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(a, b, c, d, e, W, 75, temp);
+
+#ifdef DOSTORESTATE76
+       SHA1_STORE_STATE(76)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(e, a, b, c, d, W, 76, temp);
+
+#ifdef DOSTORESTATE77
+       SHA1_STORE_STATE(77)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(d, e, a, b, c, W, 77, temp);
+
+#ifdef DOSTORESTATE78
+       SHA1_STORE_STATE(78)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(c, d, e, a, b, W, 78, temp);
+
+#ifdef DOSTORESTATE79
+       SHA1_STORE_STATE(79)
+#endif
+       SHA1COMPRESS_FULL_ROUND4_STEP(b, c, d, e, a, W, 79, temp);
+
+
+
+       ihv[0] += a; ihv[1] += b; ihv[2] += c; ihv[3] += d; ihv[4] += e;
+}
+
+
+
+
+#define SHA1_RECOMPRESS(t) \
+static void sha1recompress_fast_ ## t (uint32_t ihvin[5], uint32_t ihvout[5], const uint32_t me2[80], const uint32_t state[5]) \
+{ \
+       uint32_t a = state[0], b = state[1], c = state[2], d = state[3], e = state[4]; \
+       if (t > 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 79); \
+       if (t > 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 78); \
+       if (t > 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 77); \
+       if (t > 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 76); \
+       if (t > 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 75); \
+       if (t > 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 74); \
+       if (t > 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 73); \
+       if (t > 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 72); \
+       if (t > 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 71); \
+       if (t > 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 70); \
+       if (t > 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 69); \
+       if (t > 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 68); \
+       if (t > 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 67); \
+       if (t > 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 66); \
+       if (t > 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 65); \
+       if (t > 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(b, c, d, e, a, me2, 64); \
+       if (t > 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(c, d, e, a, b, me2, 63); \
+       if (t > 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(d, e, a, b, c, me2, 62); \
+       if (t > 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(e, a, b, c, d, me2, 61); \
+       if (t > 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP_BW(a, b, c, d, e, me2, 60); \
+       if (t > 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 59); \
+       if (t > 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 58); \
+       if (t > 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 57); \
+       if (t > 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 56); \
+       if (t > 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 55); \
+       if (t > 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 54); \
+       if (t > 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 53); \
+       if (t > 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 52); \
+       if (t > 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 51); \
+       if (t > 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 50); \
+       if (t > 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 49); \
+       if (t > 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 48); \
+       if (t > 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 47); \
+       if (t > 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 46); \
+       if (t > 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 45); \
+       if (t > 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(b, c, d, e, a, me2, 44); \
+       if (t > 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(c, d, e, a, b, me2, 43); \
+       if (t > 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(d, e, a, b, c, me2, 42); \
+       if (t > 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(e, a, b, c, d, me2, 41); \
+       if (t > 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP_BW(a, b, c, d, e, me2, 40); \
+       if (t > 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 39); \
+       if (t > 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 38); \
+       if (t > 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 37); \
+       if (t > 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 36); \
+       if (t > 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 35); \
+       if (t > 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 34); \
+       if (t > 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 33); \
+       if (t > 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 32); \
+       if (t > 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 31); \
+       if (t > 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 30); \
+       if (t > 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 29); \
+       if (t > 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 28); \
+       if (t > 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 27); \
+       if (t > 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 26); \
+       if (t > 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 25); \
+       if (t > 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(b, c, d, e, a, me2, 24); \
+       if (t > 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(c, d, e, a, b, me2, 23); \
+       if (t > 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(d, e, a, b, c, me2, 22); \
+       if (t > 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(e, a, b, c, d, me2, 21); \
+       if (t > 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP_BW(a, b, c, d, e, me2, 20); \
+       if (t > 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 19); \
+       if (t > 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 18); \
+       if (t > 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 17); \
+       if (t > 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 16); \
+       if (t > 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 15); \
+       if (t > 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 14); \
+       if (t > 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 13); \
+       if (t > 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 12); \
+       if (t > 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 11); \
+       if (t > 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 10); \
+       if (t > 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 9); \
+       if (t > 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 8); \
+       if (t > 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 7); \
+       if (t > 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 6); \
+       if (t > 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 5); \
+       if (t > 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(b, c, d, e, a, me2, 4); \
+       if (t > 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(c, d, e, a, b, me2, 3); \
+       if (t > 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(d, e, a, b, c, me2, 2); \
+       if (t > 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(e, a, b, c, d, me2, 1); \
+       if (t > 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP_BW(a, b, c, d, e, me2, 0); \
+       ihvin[0] = a; ihvin[1] = b; ihvin[2] = c; ihvin[3] = d; ihvin[4] = e; \
+       a = state[0]; b = state[1]; c = state[2]; d = state[3]; e = state[4]; \
+       if (t <= 0) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 0); \
+       if (t <= 1) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 1); \
+       if (t <= 2) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 2); \
+       if (t <= 3) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 3); \
+       if (t <= 4) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 4); \
+       if (t <= 5) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 5); \
+       if (t <= 6) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 6); \
+       if (t <= 7) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 7); \
+       if (t <= 8) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 8); \
+       if (t <= 9) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 9); \
+       if (t <= 10) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 10); \
+       if (t <= 11) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 11); \
+       if (t <= 12) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 12); \
+       if (t <= 13) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 13); \
+       if (t <= 14) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 14); \
+       if (t <= 15) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(a, b, c, d, e, me2, 15); \
+       if (t <= 16) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(e, a, b, c, d, me2, 16); \
+       if (t <= 17) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(d, e, a, b, c, me2, 17); \
+       if (t <= 18) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(c, d, e, a, b, me2, 18); \
+       if (t <= 19) HASHCLASH_SHA1COMPRESS_ROUND1_STEP(b, c, d, e, a, me2, 19); \
+       if (t <= 20) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 20); \
+       if (t <= 21) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 21); \
+       if (t <= 22) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 22); \
+       if (t <= 23) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 23); \
+       if (t <= 24) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 24); \
+       if (t <= 25) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 25); \
+       if (t <= 26) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 26); \
+       if (t <= 27) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 27); \
+       if (t <= 28) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 28); \
+       if (t <= 29) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 29); \
+       if (t <= 30) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 30); \
+       if (t <= 31) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 31); \
+       if (t <= 32) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 32); \
+       if (t <= 33) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 33); \
+       if (t <= 34) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 34); \
+       if (t <= 35) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(a, b, c, d, e, me2, 35); \
+       if (t <= 36) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(e, a, b, c, d, me2, 36); \
+       if (t <= 37) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(d, e, a, b, c, me2, 37); \
+       if (t <= 38) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(c, d, e, a, b, me2, 38); \
+       if (t <= 39) HASHCLASH_SHA1COMPRESS_ROUND2_STEP(b, c, d, e, a, me2, 39); \
+       if (t <= 40) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 40); \
+       if (t <= 41) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 41); \
+       if (t <= 42) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 42); \
+       if (t <= 43) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 43); \
+       if (t <= 44) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 44); \
+       if (t <= 45) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 45); \
+       if (t <= 46) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 46); \
+       if (t <= 47) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 47); \
+       if (t <= 48) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 48); \
+       if (t <= 49) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 49); \
+       if (t <= 50) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 50); \
+       if (t <= 51) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 51); \
+       if (t <= 52) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 52); \
+       if (t <= 53) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 53); \
+       if (t <= 54) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 54); \
+       if (t <= 55) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(a, b, c, d, e, me2, 55); \
+       if (t <= 56) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(e, a, b, c, d, me2, 56); \
+       if (t <= 57) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(d, e, a, b, c, me2, 57); \
+       if (t <= 58) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(c, d, e, a, b, me2, 58); \
+       if (t <= 59) HASHCLASH_SHA1COMPRESS_ROUND3_STEP(b, c, d, e, a, me2, 59); \
+       if (t <= 60) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 60); \
+       if (t <= 61) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 61); \
+       if (t <= 62) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 62); \
+       if (t <= 63) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 63); \
+       if (t <= 64) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 64); \
+       if (t <= 65) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 65); \
+       if (t <= 66) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 66); \
+       if (t <= 67) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 67); \
+       if (t <= 68) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 68); \
+       if (t <= 69) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 69); \
+       if (t <= 70) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 70); \
+       if (t <= 71) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 71); \
+       if (t <= 72) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 72); \
+       if (t <= 73) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 73); \
+       if (t <= 74) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 74); \
+       if (t <= 75) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(a, b, c, d, e, me2, 75); \
+       if (t <= 76) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(e, a, b, c, d, me2, 76); \
+       if (t <= 77) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(d, e, a, b, c, me2, 77); \
+       if (t <= 78) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(c, d, e, a, b, me2, 78); \
+       if (t <= 79) HASHCLASH_SHA1COMPRESS_ROUND4_STEP(b, c, d, e, a, me2, 79); \
+       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 DOSTORESTATE0
+SHA1_RECOMPRESS(0)
+#endif
+
+#ifdef DOSTORESTATE1
+SHA1_RECOMPRESS(1)
+#endif
+
+#ifdef DOSTORESTATE2
+SHA1_RECOMPRESS(2)
+#endif
+
+#ifdef DOSTORESTATE3
+SHA1_RECOMPRESS(3)
+#endif
+
+#ifdef DOSTORESTATE4
+SHA1_RECOMPRESS(4)
+#endif
+
+#ifdef DOSTORESTATE5
+SHA1_RECOMPRESS(5)
+#endif
+
+#ifdef DOSTORESTATE6
+SHA1_RECOMPRESS(6)
+#endif
+
+#ifdef DOSTORESTATE7
+SHA1_RECOMPRESS(7)
+#endif
+
+#ifdef DOSTORESTATE8
+SHA1_RECOMPRESS(8)
+#endif
+
+#ifdef DOSTORESTATE9
+SHA1_RECOMPRESS(9)
+#endif
+
+#ifdef DOSTORESTATE10
+SHA1_RECOMPRESS(10)
+#endif
+
+#ifdef DOSTORESTATE11
+SHA1_RECOMPRESS(11)
+#endif
+
+#ifdef DOSTORESTATE12
+SHA1_RECOMPRESS(12)
+#endif
+
+#ifdef DOSTORESTATE13
+SHA1_RECOMPRESS(13)
+#endif
+
+#ifdef DOSTORESTATE14
+SHA1_RECOMPRESS(14)
+#endif
+
+#ifdef DOSTORESTATE15
+SHA1_RECOMPRESS(15)
+#endif
+
+#ifdef DOSTORESTATE16
+SHA1_RECOMPRESS(16)
+#endif
+
+#ifdef DOSTORESTATE17
+SHA1_RECOMPRESS(17)
+#endif
+
+#ifdef DOSTORESTATE18
+SHA1_RECOMPRESS(18)
+#endif
+
+#ifdef DOSTORESTATE19
+SHA1_RECOMPRESS(19)
+#endif
+
+#ifdef DOSTORESTATE20
+SHA1_RECOMPRESS(20)
+#endif
+
+#ifdef DOSTORESTATE21
+SHA1_RECOMPRESS(21)
+#endif
+
+#ifdef DOSTORESTATE22
+SHA1_RECOMPRESS(22)
+#endif
+
+#ifdef DOSTORESTATE23
+SHA1_RECOMPRESS(23)
+#endif
+
+#ifdef DOSTORESTATE24
+SHA1_RECOMPRESS(24)
+#endif
+
+#ifdef DOSTORESTATE25
+SHA1_RECOMPRESS(25)
+#endif
+
+#ifdef DOSTORESTATE26
+SHA1_RECOMPRESS(26)
+#endif
+
+#ifdef DOSTORESTATE27
+SHA1_RECOMPRESS(27)
+#endif
+
+#ifdef DOSTORESTATE28
+SHA1_RECOMPRESS(28)
+#endif
+
+#ifdef DOSTORESTATE29
+SHA1_RECOMPRESS(29)
+#endif
+
+#ifdef DOSTORESTATE30
+SHA1_RECOMPRESS(30)
+#endif
+
+#ifdef DOSTORESTATE31
+SHA1_RECOMPRESS(31)
+#endif
+
+#ifdef DOSTORESTATE32
+SHA1_RECOMPRESS(32)
+#endif
+
+#ifdef DOSTORESTATE33
+SHA1_RECOMPRESS(33)
+#endif
+
+#ifdef DOSTORESTATE34
+SHA1_RECOMPRESS(34)
+#endif
+
+#ifdef DOSTORESTATE35
+SHA1_RECOMPRESS(35)
+#endif
+
+#ifdef DOSTORESTATE36
+SHA1_RECOMPRESS(36)
+#endif
+
+#ifdef DOSTORESTATE37
+SHA1_RECOMPRESS(37)
+#endif
+
+#ifdef DOSTORESTATE38
+SHA1_RECOMPRESS(38)
+#endif
+
+#ifdef DOSTORESTATE39
+SHA1_RECOMPRESS(39)
+#endif
+
+#ifdef DOSTORESTATE40
+SHA1_RECOMPRESS(40)
+#endif
+
+#ifdef DOSTORESTATE41
+SHA1_RECOMPRESS(41)
+#endif
+
+#ifdef DOSTORESTATE42
+SHA1_RECOMPRESS(42)
+#endif
+
+#ifdef DOSTORESTATE43
+SHA1_RECOMPRESS(43)
+#endif
+
+#ifdef DOSTORESTATE44
+SHA1_RECOMPRESS(44)
+#endif
+
+#ifdef DOSTORESTATE45
+SHA1_RECOMPRESS(45)
+#endif
+
+#ifdef DOSTORESTATE46
+SHA1_RECOMPRESS(46)
+#endif
+
+#ifdef DOSTORESTATE47
+SHA1_RECOMPRESS(47)
+#endif
+
+#ifdef DOSTORESTATE48
+SHA1_RECOMPRESS(48)
+#endif
+
+#ifdef DOSTORESTATE49
+SHA1_RECOMPRESS(49)
+#endif
+
+#ifdef DOSTORESTATE50
+SHA1_RECOMPRESS(50)
+#endif
+
+#ifdef DOSTORESTATE51
+SHA1_RECOMPRESS(51)
+#endif
+
+#ifdef DOSTORESTATE52
+SHA1_RECOMPRESS(52)
+#endif
+
+#ifdef DOSTORESTATE53
+SHA1_RECOMPRESS(53)
+#endif
+
+#ifdef DOSTORESTATE54
+SHA1_RECOMPRESS(54)
+#endif
+
+#ifdef DOSTORESTATE55
+SHA1_RECOMPRESS(55)
+#endif
+
+#ifdef DOSTORESTATE56
+SHA1_RECOMPRESS(56)
+#endif
+
+#ifdef DOSTORESTATE57
+SHA1_RECOMPRESS(57)
+#endif
+
+#ifdef DOSTORESTATE58
+SHA1_RECOMPRESS(58)
+#endif
+
+#ifdef DOSTORESTATE59
+SHA1_RECOMPRESS(59)
+#endif
+
+#ifdef DOSTORESTATE60
+SHA1_RECOMPRESS(60)
+#endif
+
+#ifdef DOSTORESTATE61
+SHA1_RECOMPRESS(61)
+#endif
+
+#ifdef DOSTORESTATE62
+SHA1_RECOMPRESS(62)
+#endif
+
+#ifdef DOSTORESTATE63
+SHA1_RECOMPRESS(63)
+#endif
+
+#ifdef DOSTORESTATE64
+SHA1_RECOMPRESS(64)
+#endif
+
+#ifdef DOSTORESTATE65
+SHA1_RECOMPRESS(65)
+#endif
+
+#ifdef DOSTORESTATE66
+SHA1_RECOMPRESS(66)
+#endif
+
+#ifdef DOSTORESTATE67
+SHA1_RECOMPRESS(67)
+#endif
+
+#ifdef DOSTORESTATE68
+SHA1_RECOMPRESS(68)
+#endif
+
+#ifdef DOSTORESTATE69
+SHA1_RECOMPRESS(69)
+#endif
+
+#ifdef DOSTORESTATE70
+SHA1_RECOMPRESS(70)
+#endif
+
+#ifdef DOSTORESTATE71
+SHA1_RECOMPRESS(71)
+#endif
+
+#ifdef DOSTORESTATE72
+SHA1_RECOMPRESS(72)
+#endif
+
+#ifdef DOSTORESTATE73
+SHA1_RECOMPRESS(73)
+#endif
+
+#ifdef DOSTORESTATE74
+SHA1_RECOMPRESS(74)
+#endif
+
+#ifdef DOSTORESTATE75
+SHA1_RECOMPRESS(75)
+#endif
+
+#ifdef DOSTORESTATE76
+SHA1_RECOMPRESS(76)
+#endif
+
+#ifdef DOSTORESTATE77
+SHA1_RECOMPRESS(77)
+#endif
+
+#ifdef DOSTORESTATE78
+SHA1_RECOMPRESS(78)
+#endif
+
+#ifdef DOSTORESTATE79
+SHA1_RECOMPRESS(79)
+#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)
+       {
+#ifdef DOSTORESTATE0
+       case 0:
+               sha1recompress_fast_0(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE1
+       case 1:
+               sha1recompress_fast_1(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE2
+       case 2:
+               sha1recompress_fast_2(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE3
+       case 3:
+               sha1recompress_fast_3(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE4
+       case 4:
+               sha1recompress_fast_4(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE5
+       case 5:
+               sha1recompress_fast_5(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE6
+       case 6:
+               sha1recompress_fast_6(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE7
+       case 7:
+               sha1recompress_fast_7(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE8
+       case 8:
+               sha1recompress_fast_8(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE9
+       case 9:
+               sha1recompress_fast_9(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE10
+       case 10:
+               sha1recompress_fast_10(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE11
+       case 11:
+               sha1recompress_fast_11(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE12
+       case 12:
+               sha1recompress_fast_12(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE13
+       case 13:
+               sha1recompress_fast_13(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE14
+       case 14:
+               sha1recompress_fast_14(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE15
+       case 15:
+               sha1recompress_fast_15(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE16
+       case 16:
+               sha1recompress_fast_16(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE17
+       case 17:
+               sha1recompress_fast_17(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE18
+       case 18:
+               sha1recompress_fast_18(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE19
+       case 19:
+               sha1recompress_fast_19(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE20
+       case 20:
+               sha1recompress_fast_20(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE21
+       case 21:
+               sha1recompress_fast_21(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE22
+       case 22:
+               sha1recompress_fast_22(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE23
+       case 23:
+               sha1recompress_fast_23(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE24
+       case 24:
+               sha1recompress_fast_24(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE25
+       case 25:
+               sha1recompress_fast_25(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE26
+       case 26:
+               sha1recompress_fast_26(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE27
+       case 27:
+               sha1recompress_fast_27(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE28
+       case 28:
+               sha1recompress_fast_28(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE29
+       case 29:
+               sha1recompress_fast_29(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE30
+       case 30:
+               sha1recompress_fast_30(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE31
+       case 31:
+               sha1recompress_fast_31(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE32
+       case 32:
+               sha1recompress_fast_32(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE33
+       case 33:
+               sha1recompress_fast_33(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE34
+       case 34:
+               sha1recompress_fast_34(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE35
+       case 35:
+               sha1recompress_fast_35(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE36
+       case 36:
+               sha1recompress_fast_36(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE37
+       case 37:
+               sha1recompress_fast_37(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE38
+       case 38:
+               sha1recompress_fast_38(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE39
+       case 39:
+               sha1recompress_fast_39(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE40
+       case 40:
+               sha1recompress_fast_40(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE41
+       case 41:
+               sha1recompress_fast_41(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE42
+       case 42:
+               sha1recompress_fast_42(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE43
+       case 43:
+               sha1recompress_fast_43(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE44
+       case 44:
+               sha1recompress_fast_44(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE45
+       case 45:
+               sha1recompress_fast_45(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE46
+       case 46:
+               sha1recompress_fast_46(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE47
+       case 47:
+               sha1recompress_fast_47(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE48
+       case 48:
+               sha1recompress_fast_48(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE49
+       case 49:
+               sha1recompress_fast_49(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE50
+       case 50:
+               sha1recompress_fast_50(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE51
+       case 51:
+               sha1recompress_fast_51(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE52
+       case 52:
+               sha1recompress_fast_52(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE53
+       case 53:
+               sha1recompress_fast_53(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE54
+       case 54:
+               sha1recompress_fast_54(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE55
+       case 55:
+               sha1recompress_fast_55(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE56
+       case 56:
+               sha1recompress_fast_56(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE57
+       case 57:
+               sha1recompress_fast_57(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE58
+       case 58:
+               sha1recompress_fast_58(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE59
+       case 59:
+               sha1recompress_fast_59(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE60
+       case 60:
+               sha1recompress_fast_60(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE61
+       case 61:
+               sha1recompress_fast_61(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE62
+       case 62:
+               sha1recompress_fast_62(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE63
+       case 63:
+               sha1recompress_fast_63(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE64
+       case 64:
+               sha1recompress_fast_64(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE65
+       case 65:
+               sha1recompress_fast_65(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE66
+       case 66:
+               sha1recompress_fast_66(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE67
+       case 67:
+               sha1recompress_fast_67(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE68
+       case 68:
+               sha1recompress_fast_68(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE69
+       case 69:
+               sha1recompress_fast_69(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE70
+       case 70:
+               sha1recompress_fast_70(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE71
+       case 71:
+               sha1recompress_fast_71(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE72
+       case 72:
+               sha1recompress_fast_72(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE73
+       case 73:
+               sha1recompress_fast_73(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE74
+       case 74:
+               sha1recompress_fast_74(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE75
+       case 75:
+               sha1recompress_fast_75(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE76
+       case 76:
+               sha1recompress_fast_76(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE77
+       case 77:
+               sha1recompress_fast_77(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE78
+       case 78:
+               sha1recompress_fast_78(ihvin, ihvout, me2, state);
+               break;
+#endif
+#ifdef DOSTORESTATE79
+       case 79:
+               sha1recompress_fast_79(ihvin, ihvout, me2, state);
+               break;
+#endif
+       default:
+               abort();
+       }
+
+}
+
+
+
+static void sha1_process(SHA1_CTX* ctx, const uint32_t block[16])
+{
+       unsigned i, j;
+       uint32_t ubc_dv_mask[DVMASKSIZE] = { 0xFFFFFFFF };
+       uint32_t ihvtmp[5];
+
+       ctx->ihv1[0] = ctx->ihv[0];
+       ctx->ihv1[1] = ctx->ihv[1];
+       ctx->ihv1[2] = ctx->ihv[2];
+       ctx->ihv1[3] = ctx->ihv[3];
+       ctx->ihv1[4] = ctx->ihv[4];
+
+       sha1_compression_states(ctx->ihv, block, ctx->m1, ctx->states);
+
+       if (ctx->detect_coll)
+       {
+               if (ctx->ubc_check)
+               {
+                       ubc_check(ctx->m1, ubc_dv_mask);
+               }
+
+               if (ubc_dv_mask[0] != 0)
+               {
+                       for (i = 0; sha1_dvs[i].dvType != 0; ++i)
+                       {
+                               if (ubc_dv_mask[0] & ((uint32_t)(1) << sha1_dvs[i].maskb))
+                               {
+                                       for (j = 0; j < 80; ++j)
+                                               ctx->m2[j] = ctx->m1[j] ^ sha1_dvs[i].dm[j];
+
+                                       sha1_recompression_step(sha1_dvs[i].testt, ctx->ihv2, ihvtmp, ctx->m2, ctx->states[sha1_dvs[i].testt]);
+
+                                       /* to verify SHA-1 collision detection code with collisions for reduced-step SHA-1 */
+                                       if ((0 == ((ihvtmp[0] ^ ctx->ihv[0]) | (ihvtmp[1] ^ ctx->ihv[1]) | (ihvtmp[2] ^ ctx->ihv[2]) | (ihvtmp[3] ^ ctx->ihv[3]) | (ihvtmp[4] ^ ctx->ihv[4])))
+                                               || (ctx->reduced_round_coll && 0==((ctx->ihv1[0] ^ ctx->ihv2[0]) | (ctx->ihv1[1] ^ ctx->ihv2[1]) | (ctx->ihv1[2] ^ ctx->ihv2[2]) | (ctx->ihv1[3] ^ ctx->ihv2[3]) | (ctx->ihv1[4] ^ ctx->ihv2[4]))))
+                                       {
+                                               ctx->found_collision = 1;
+
+                                               if (ctx->safe_hash)
+                                               {
+                                                       sha1_compression_W(ctx->ihv, ctx->m1);
+                                                       sha1_compression_W(ctx->ihv, ctx->m1);
+                                               }
+
+                                               break;
+                                       }
+                               }
+                       }
+               }
+       }
+}
+
+void SHA1DCInit(SHA1_CTX* ctx)
+{
+       ctx->total = 0;
+       ctx->ihv[0] = 0x67452301;
+       ctx->ihv[1] = 0xEFCDAB89;
+       ctx->ihv[2] = 0x98BADCFE;
+       ctx->ihv[3] = 0x10325476;
+       ctx->ihv[4] = 0xC3D2E1F0;
+       ctx->found_collision = 0;
+       ctx->safe_hash = 0;
+       ctx->ubc_check = 1;
+       ctx->detect_coll = 1;
+       ctx->reduced_round_coll = 0;
+       ctx->callback = NULL;
+}
+
+void SHA1DCSetSafeHash(SHA1_CTX* ctx, int safehash)
+{
+       if (safehash)
+               ctx->safe_hash = 1;
+       else
+               ctx->safe_hash = 0;
+}
+
+
+void SHA1DCSetUseUBC(SHA1_CTX* ctx, int ubc_check)
+{
+       if (ubc_check)
+               ctx->ubc_check = 1;
+       else
+               ctx->ubc_check = 0;
+}
+
+void SHA1DCSetUseDetectColl(SHA1_CTX* ctx, int detect_coll)
+{
+       if (detect_coll)
+               ctx->detect_coll = 1;
+       else
+               ctx->detect_coll = 0;
+}
+
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX* ctx, int reduced_round_coll)
+{
+       if (reduced_round_coll)
+               ctx->reduced_round_coll = 1;
+       else
+               ctx->reduced_round_coll = 0;
+}
+
+void SHA1DCSetCallback(SHA1_CTX* ctx, collision_block_callback callback)
+{
+       ctx->callback = callback;
+}
+
+void SHA1DCUpdate(SHA1_CTX* ctx, const char* buf, size_t len)
+{
+       unsigned left, fill;
+       if (len == 0)
+               return;
+
+       left = ctx->total & 63;
+       fill = 64 - left;
+
+       if (left && len >= fill)
+       {
+               ctx->total += fill;
+               memcpy(ctx->buffer + left, buf, fill);
+               sha1_process(ctx, (uint32_t*)(ctx->buffer));
+               buf += fill;
+               len -= fill;
+               left = 0;
+       }
+       while (len >= 64)
+       {
+               ctx->total += 64;
+               sha1_process(ctx, (uint32_t*)(buf));
+               buf += 64;
+               len -= 64;
+       }
+       if (len > 0)
+       {
+               ctx->total += len;
+               memcpy(ctx->buffer + left, buf, len);
+       }
+}
+
+static const unsigned char sha1_padding[64] =
+{
+       0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+       0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int SHA1DCFinal(unsigned char output[20], SHA1_CTX *ctx)
+{
+       uint32_t last = ctx->total & 63;
+       uint32_t padn = (last < 56) ? (56 - last) : (120 - last);
+       uint64_t total;
+       SHA1DCUpdate(ctx, (const char*)(sha1_padding), padn);
+
+       total = ctx->total - padn;
+       total <<= 3;
+       ctx->buffer[56] = (unsigned char)(total >> 56);
+       ctx->buffer[57] = (unsigned char)(total >> 48);
+       ctx->buffer[58] = (unsigned char)(total >> 40);
+       ctx->buffer[59] = (unsigned char)(total >> 32);
+       ctx->buffer[60] = (unsigned char)(total >> 24);
+       ctx->buffer[61] = (unsigned char)(total >> 16);
+       ctx->buffer[62] = (unsigned char)(total >> 8);
+       ctx->buffer[63] = (unsigned char)(total);
+       sha1_process(ctx, (uint32_t*)(ctx->buffer));
+       output[0] = (unsigned char)(ctx->ihv[0] >> 24);
+       output[1] = (unsigned char)(ctx->ihv[0] >> 16);
+       output[2] = (unsigned char)(ctx->ihv[0] >> 8);
+       output[3] = (unsigned char)(ctx->ihv[0]);
+       output[4] = (unsigned char)(ctx->ihv[1] >> 24);
+       output[5] = (unsigned char)(ctx->ihv[1] >> 16);
+       output[6] = (unsigned char)(ctx->ihv[1] >> 8);
+       output[7] = (unsigned char)(ctx->ihv[1]);
+       output[8] = (unsigned char)(ctx->ihv[2] >> 24);
+       output[9] = (unsigned char)(ctx->ihv[2] >> 16);
+       output[10] = (unsigned char)(ctx->ihv[2] >> 8);
+       output[11] = (unsigned char)(ctx->ihv[2]);
+       output[12] = (unsigned char)(ctx->ihv[3] >> 24);
+       output[13] = (unsigned char)(ctx->ihv[3] >> 16);
+       output[14] = (unsigned char)(ctx->ihv[3] >> 8);
+       output[15] = (unsigned char)(ctx->ihv[3]);
+       output[16] = (unsigned char)(ctx->ihv[4] >> 24);
+       output[17] = (unsigned char)(ctx->ihv[4] >> 16);
+       output[18] = (unsigned char)(ctx->ihv[4] >> 8);
+       output[19] = (unsigned char)(ctx->ihv[4]);
+       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);
+}
diff --git a/sha1dc/sha1.h b/sha1dc/sha1.h
new file mode 100644 (file)
index 0000000..bd8bd92
--- /dev/null
@@ -0,0 +1,122 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+#ifndef SHA1DC_SHA1_H
+#define SHA1DC_SHA1_H
+
+#if defined(__cplusplus)
+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]); */
+
+/* same as sha1_compression_W, but 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
+*/
+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: */
+/* 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 */
+typedef struct {
+       uint64_t total;
+       uint32_t ihv[5];
+       unsigned char buffer[64];
+       int found_collision;
+       int safe_hash;
+       int detect_coll;
+       int ubc_check;
+       int reduced_round_coll;
+       collision_block_callback callback;
+
+       uint32_t ihv1[5];
+       uint32_t ihv2[5];
+       uint32_t m1[80];
+       uint32_t m2[80];
+       uint32_t states[80][5];
+} SHA1_CTX;
+
+/* 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
+*/
+void SHA1DCSetSafeHash(SHA1_CTX*, int);
+
+/* 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 */
+void SHA1DCSetUseDetectColl(SHA1_CTX*, int);
+
+/* function to disable or enable the detection of reduced-round SHA-1 collisions */
+/* disabled by default */
+void SHA1DCSetDetectReducedRoundCollision(SHA1_CTX*, int);
+
+/* function to set a callback function, pass NULL to disable */
+/* by default no callback set */
+void SHA1DCSetCallback(SHA1_CTX*, collision_block_callback);
+
+/* update SHA-1 context with buffer contents */
+void SHA1DCUpdate(SHA1_CTX*, const char*, size_t);
+
+/* obtain SHA-1 hash from SHA-1 context */
+/* 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 */
diff --git a/sha1dc/ubc_check.c b/sha1dc/ubc_check.c
new file mode 100644 (file)
index 0000000..089dd47
--- /dev/null
@@ -0,0 +1,363 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+/*
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+//
+// ubc_check is programmatically generated and the unavoidable bitconditions have been hardcoded
+// a directly verifiable version named ubc_check_verify can be found in ubc_check_verify.c
+// 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"
+
+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;
+static const uint32_t DV_I_45_0_bit    = (uint32_t)(1) << 2;
+static const uint32_t DV_I_46_0_bit    = (uint32_t)(1) << 3;
+static const uint32_t DV_I_46_2_bit    = (uint32_t)(1) << 4;
+static const uint32_t DV_I_47_0_bit    = (uint32_t)(1) << 5;
+static const uint32_t DV_I_47_2_bit    = (uint32_t)(1) << 6;
+static const uint32_t DV_I_48_0_bit    = (uint32_t)(1) << 7;
+static const uint32_t DV_I_48_2_bit    = (uint32_t)(1) << 8;
+static const uint32_t DV_I_49_0_bit    = (uint32_t)(1) << 9;
+static const uint32_t DV_I_49_2_bit    = (uint32_t)(1) << 10;
+static const uint32_t DV_I_50_0_bit    = (uint32_t)(1) << 11;
+static const uint32_t DV_I_50_2_bit    = (uint32_t)(1) << 12;
+static const uint32_t DV_I_51_0_bit    = (uint32_t)(1) << 13;
+static const uint32_t DV_I_51_2_bit    = (uint32_t)(1) << 14;
+static const uint32_t DV_I_52_0_bit    = (uint32_t)(1) << 15;
+static const uint32_t DV_II_45_0_bit   = (uint32_t)(1) << 16;
+static const uint32_t DV_II_46_0_bit   = (uint32_t)(1) << 17;
+static const uint32_t DV_II_46_2_bit   = (uint32_t)(1) << 18;
+static const uint32_t DV_II_47_0_bit   = (uint32_t)(1) << 19;
+static const uint32_t DV_II_48_0_bit   = (uint32_t)(1) << 20;
+static const uint32_t DV_II_49_0_bit   = (uint32_t)(1) << 21;
+static const uint32_t DV_II_49_2_bit   = (uint32_t)(1) << 22;
+static const uint32_t DV_II_50_0_bit   = (uint32_t)(1) << 23;
+static const uint32_t DV_II_50_2_bit   = (uint32_t)(1) << 24;
+static const uint32_t DV_II_51_0_bit   = (uint32_t)(1) << 25;
+static const uint32_t DV_II_51_2_bit   = (uint32_t)(1) << 26;
+static const uint32_t DV_II_52_0_bit   = (uint32_t)(1) << 27;
+static const uint32_t DV_II_53_0_bit   = (uint32_t)(1) << 28;
+static const uint32_t DV_II_54_0_bit   = (uint32_t)(1) << 29;
+static const uint32_t DV_II_55_0_bit   = (uint32_t)(1) << 30;
+static const uint32_t DV_II_56_0_bit   = (uint32_t)(1) << 31;
+
+dv_info_t sha1_dvs[] =
+{
+  {1,43,0,58,0,0, { 0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161,0x80000599 } }
+, {1,44,0,58,0,1, { 0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803,0x80000161 } }
+, {1,45,0,58,0,2, { 0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c,0x00000803 } }
+, {1,46,0,58,0,3, { 0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6,0x8000004c } }
+, {1,46,2,58,0,4, { 0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a,0x00000132 } }
+, {1,47,0,58,0,5, { 0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408,0x800000e6 } }
+, {1,47,2,58,0,6, { 0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020,0x0000039a } }
+, {1,48,0,58,0,7, { 0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164,0x00000408 } }
+, {1,48,2,58,0,8, { 0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590,0x00001020 } }
+, {1,49,0,58,0,9, { 0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018,0x00000164 } }
+, {1,49,2,58,0,10, { 0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060,0x00000590 } }
+, {1,50,0,65,0,11, { 0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202,0x00000018 } }
+, {1,50,2,65,0,12, { 0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a,0x00000060 } }
+, {1,51,0,65,0,13, { 0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012,0x80000202 } }
+, {1,51,2,65,0,14, { 0xa0000003,0x20000030,0x60000000,0xe000002a,0x20000043,0xb0000040,0xd0000053,0xd0000022,0x20000000,0x60000032,0x60000043,0x20000040,0xe0000042,0x60000002,0x80000001,0x00000020,0x00000003,0x40000052,0x40000040,0xe0000052,0xa0000000,0x80000040,0x20000001,0x20000060,0x80000001,0x40000042,0xc0000043,0x40000022,0x00000003,0x40000042,0xc0000043,0xc0000022,0x00000001,0x40000002,0xc0000043,0x40000062,0x80000001,0x40000042,0x40000042,0x40000002,0x00000002,0x00000040,0x80000002,0x80000000,0x80000002,0x80000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000000,0x00000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000101,0x00000009,0x00000012,0x00000202,0x0000001a,0x00000124,0x0000040c,0x00000026,0x0000004a,0x0000080a } }
+, {1,52,0,65,0,15, { 0x04000010,0xe8000000,0x0800000c,0x18000000,0xb800000a,0xc8000010,0x2c000010,0xf4000014,0xb4000008,0x08000000,0x9800000c,0xd8000010,0x08000010,0xb8000010,0x98000000,0x60000000,0x00000008,0xc0000000,0x90000014,0x10000010,0xb8000014,0x28000000,0x20000010,0x48000000,0x08000018,0x60000000,0x90000010,0xf0000010,0x90000008,0xc0000000,0x90000010,0xf0000010,0xb0000008,0x40000000,0x90000000,0xf0000010,0x90000018,0x60000000,0x90000010,0x90000010,0x90000000,0x80000000,0x00000010,0xa0000000,0x20000000,0xa0000000,0x20000010,0x00000000,0x20000010,0x20000000,0x00000010,0x20000000,0x00000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000040,0x40000002,0x80000004,0x80000080,0x80000006,0x00000049,0x00000103,0x80000009,0x80000012 } }
+, {2,45,0,58,0,16, { 0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054,0x00000967 } }
+, {2,46,0,58,0,17, { 0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4,0x80000054 } }
+, {2,46,2,58,0,18, { 0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6,0x0000106a,0x00000b90,0x00000152 } }
+, {2,47,0,58,0,19, { 0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a,0x000002e4 } }
+, {2,48,0,58,0,20, { 0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d,0x8000041a } }
+, {2,49,0,58,0,21, { 0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b,0x8000016d } }
+, {2,49,2,58,0,22, { 0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c,0x000005b6 } }
+, {2,50,0,65,0,23, { 0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b,0x0000011b } }
+, {2,50,2,65,0,24, { 0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e,0x0000046c } }
+, {2,51,0,65,0,25, { 0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014,0x8000024b } }
+, {2,51,2,65,0,26, { 0x00000043,0xd0000072,0xf0000010,0xf000006a,0x80000040,0x90000070,0xb0000053,0x30000008,0x00000043,0xd0000072,0xb0000010,0xf0000062,0xc0000042,0x00000030,0xe0000042,0x20000060,0xe0000041,0x20000050,0xc0000041,0xe0000072,0xa0000003,0xc0000012,0x60000041,0xc0000032,0x20000001,0xc0000002,0xe0000042,0x60000042,0x80000002,0x00000000,0x00000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000000,0x00000040,0x80000001,0x00000060,0x80000003,0x40000002,0xc0000040,0xc0000002,0x80000000,0x80000000,0x80000002,0x00000040,0x00000002,0x80000000,0x80000000,0x80000000,0x00000002,0x00000040,0x00000000,0x80000040,0x80000002,0x00000000,0x80000000,0x80000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000004,0x00000080,0x00000004,0x00000009,0x00000105,0x00000089,0x00000016,0x0000020b,0x0000011b,0x0000012d,0x0000041e,0x00000224,0x00000050,0x0000092e } }
+, {2,52,0,65,0,27, { 0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089,0x00000014 } }
+, {2,53,0,65,0,28, { 0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107,0x00000089 } }
+, {2,54,0,65,0,29, { 0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b,0x80000107 } }
+, {2,55,0,65,0,30, { 0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046,0x4000004b } }
+, {2,56,0,65,0,31, { 0x2600001a,0x00000010,0x0400001c,0xcc000014,0x0c000002,0xc0000010,0xb400001c,0x3c000004,0xbc00001a,0x20000010,0x2400001c,0xec000014,0x0c000002,0xc0000010,0xb400001c,0x2c000004,0xbc000018,0xb0000010,0x0000000c,0xb8000010,0x08000018,0x78000010,0x08000014,0x70000010,0xb800001c,0xe8000000,0xb0000004,0x58000010,0xb000000c,0x48000000,0xb0000000,0xb8000010,0x98000010,0xa0000000,0x00000000,0x00000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0x20000000,0x00000010,0x60000000,0x00000018,0xe0000000,0x90000000,0x30000010,0xb0000000,0x20000000,0x20000000,0xa0000000,0x00000010,0x80000000,0x20000000,0x20000000,0x20000000,0x80000000,0x00000010,0x00000000,0x20000010,0xa0000000,0x00000000,0x20000000,0x20000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000001,0x00000020,0x00000001,0x40000002,0x40000041,0x40000022,0x80000005,0xc0000082,0xc0000046 } }
+, {0,0,0,0,0,0, {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
+};
+void ubc_check(const uint32_t W[80], uint32_t dvmask[1])
+{
+       uint32_t mask = ~((uint32_t)(0));
+       mask &= (((((W[44]^W[45])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_I_51_0_bit|DV_I_52_0_bit|DV_II_45_0_bit|DV_II_46_0_bit|DV_II_50_0_bit|DV_II_51_0_bit));
+       mask &= (((((W[49]^W[50])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_II_45_0_bit|DV_II_50_0_bit|DV_II_51_0_bit|DV_II_55_0_bit|DV_II_56_0_bit));
+       mask &= (((((W[48]^W[49])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_52_0_bit|DV_II_49_0_bit|DV_II_50_0_bit|DV_II_54_0_bit|DV_II_55_0_bit));
+       mask &= ((((W[47]^(W[50]>>25))&(1<<4))-(1<<4)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_51_0_bit|DV_II_56_0_bit));
+       mask &= (((((W[47]^W[48])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_51_0_bit|DV_II_48_0_bit|DV_II_49_0_bit|DV_II_53_0_bit|DV_II_54_0_bit));
+       mask &= (((((W[46]>>4)^(W[49]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_50_0_bit|DV_II_55_0_bit));
+       mask &= (((((W[46]^W[47])>>29)&1)-1) | ~(DV_I_43_0_bit|DV_I_50_0_bit|DV_II_47_0_bit|DV_II_48_0_bit|DV_II_52_0_bit|DV_II_53_0_bit));
+       mask &= (((((W[45]>>4)^(W[48]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit|DV_II_49_0_bit|DV_II_54_0_bit));
+       mask &= (((((W[45]^W[46])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_51_0_bit|DV_II_52_0_bit));
+       mask &= (((((W[44]>>4)^(W[47]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_53_0_bit));
+       mask &= (((((W[43]>>4)^(W[46]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_52_0_bit));
+       mask &= (((((W[43]^W[44])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_I_50_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_49_0_bit|DV_II_50_0_bit));
+       mask &= (((((W[42]>>4)^(W[45]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_51_0_bit));
+       mask &= (((((W[41]>>4)^(W[44]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_50_0_bit));
+       mask &= (((((W[40]^W[41])>>29)&1)-1) | ~(DV_I_44_0_bit|DV_I_47_0_bit|DV_I_48_0_bit|DV_II_46_0_bit|DV_II_47_0_bit|DV_II_56_0_bit));
+       mask &= (((((W[54]^W[55])>>29)&1)-1) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_50_0_bit|DV_II_55_0_bit|DV_II_56_0_bit));
+       mask &= (((((W[53]^W[54])>>29)&1)-1) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_49_0_bit|DV_II_54_0_bit|DV_II_55_0_bit));
+       mask &= (((((W[52]^W[53])>>29)&1)-1) | ~(DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit|DV_II_53_0_bit|DV_II_54_0_bit));
+       mask &= ((((W[50]^(W[53]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_48_0_bit|DV_II_54_0_bit));
+       mask &= (((((W[50]^W[51])>>29)&1)-1) | ~(DV_I_47_0_bit|DV_II_46_0_bit|DV_II_51_0_bit|DV_II_52_0_bit|DV_II_56_0_bit));
+       mask &= ((((W[49]^(W[52]>>25))&(1<<4))-(1<<4)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit|DV_II_47_0_bit|DV_II_53_0_bit));
+       mask &= ((((W[48]^(W[51]>>25))&(1<<4))-(1<<4)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit|DV_II_46_0_bit|DV_II_52_0_bit));
+       mask &= (((((W[42]^W[43])>>29)&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_I_50_0_bit|DV_II_48_0_bit|DV_II_49_0_bit));
+       mask &= (((((W[41]^W[42])>>29)&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_I_49_0_bit|DV_II_47_0_bit|DV_II_48_0_bit));
+       mask &= (((((W[40]>>4)^(W[43]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_50_0_bit|DV_II_49_0_bit|DV_II_56_0_bit));
+       mask &= (((((W[39]>>4)^(W[42]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_49_0_bit|DV_II_48_0_bit|DV_II_55_0_bit));
+       if (mask & (DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit))
+               mask &= (((((W[38]>>4)^(W[41]>>29))&1)-1) | ~(DV_I_44_0_bit|DV_I_48_0_bit|DV_II_47_0_bit|DV_II_54_0_bit|DV_II_56_0_bit));
+       mask &= (((((W[37]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_43_0_bit|DV_I_47_0_bit|DV_II_46_0_bit|DV_II_53_0_bit|DV_II_55_0_bit));
+       if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit))
+               mask &= (((((W[55]^W[56])>>29)&1)-1) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_51_0_bit|DV_II_56_0_bit));
+       if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit))
+               mask &= ((((W[52]^(W[55]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_50_0_bit|DV_II_56_0_bit));
+       if (mask & (DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit))
+               mask &= ((((W[51]^(W[54]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_47_0_bit|DV_II_49_0_bit|DV_II_55_0_bit));
+       if (mask & (DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit))
+               mask &= (((((W[51]^W[52])>>29)&1)-1) | ~(DV_I_48_0_bit|DV_II_47_0_bit|DV_II_52_0_bit|DV_II_53_0_bit));
+       if (mask & (DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit))
+               mask &= (((((W[36]>>4)^(W[40]>>29))&1)-1) | ~(DV_I_46_0_bit|DV_I_49_0_bit|DV_II_45_0_bit|DV_II_48_0_bit));
+       if (mask & (DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit))
+               mask &= ((0-(((W[53]^W[56])>>29)&1)) | ~(DV_I_52_0_bit|DV_II_48_0_bit|DV_II_49_0_bit));
+       if (mask & (DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit))
+               mask &= ((0-(((W[51]^W[54])>>29)&1)) | ~(DV_I_50_0_bit|DV_II_46_0_bit|DV_II_47_0_bit));
+       if (mask & (DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit))
+               mask &= ((0-(((W[50]^W[52])>>29)&1)) | ~(DV_I_49_0_bit|DV_I_51_0_bit|DV_II_45_0_bit));
+       if (mask & (DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit))
+               mask &= ((0-(((W[49]^W[51])>>29)&1)) | ~(DV_I_48_0_bit|DV_I_50_0_bit|DV_I_52_0_bit));
+       if (mask & (DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit))
+               mask &= ((0-(((W[48]^W[50])>>29)&1)) | ~(DV_I_47_0_bit|DV_I_49_0_bit|DV_I_51_0_bit));
+       if (mask & (DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit))
+               mask &= ((0-(((W[47]^W[49])>>29)&1)) | ~(DV_I_46_0_bit|DV_I_48_0_bit|DV_I_50_0_bit));
+       if (mask & (DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit))
+               mask &= ((0-(((W[46]^W[48])>>29)&1)) | ~(DV_I_45_0_bit|DV_I_47_0_bit|DV_I_49_0_bit));
+       mask &= ((((W[45]^W[47])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit|DV_I_51_2_bit));
+       if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit))
+               mask &= ((0-(((W[45]^W[47])>>29)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_I_48_0_bit));
+       mask &= (((((W[44]^W[46])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit|DV_I_50_2_bit));
+       if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit))
+               mask &= ((0-(((W[44]^W[46])>>29)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_I_47_0_bit));
+       mask &= ((0-((W[41]^(W[42]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_II_46_2_bit|DV_II_51_2_bit));
+       mask &= ((0-((W[40]^(W[41]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_51_2_bit|DV_II_50_2_bit));
+       if (mask & (DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit))
+               mask &= ((0-(((W[40]^W[42])>>4)&1)) | ~(DV_I_44_0_bit|DV_I_46_0_bit|DV_II_56_0_bit));
+       mask &= ((0-((W[39]^(W[40]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_50_2_bit|DV_II_49_2_bit));
+       if (mask & (DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit))
+               mask &= ((0-(((W[39]^W[41])>>4)&1)) | ~(DV_I_43_0_bit|DV_I_45_0_bit|DV_II_55_0_bit));
+       if (mask & (DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit))
+               mask &= ((0-(((W[38]^W[40])>>4)&1)) | ~(DV_I_44_0_bit|DV_II_54_0_bit|DV_II_56_0_bit));
+       if (mask & (DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit))
+               mask &= ((0-(((W[37]^W[39])>>4)&1)) | ~(DV_I_43_0_bit|DV_II_53_0_bit|DV_II_55_0_bit));
+       mask &= ((0-((W[36]^(W[37]>>5))&(1<<1))) | ~(DV_I_47_2_bit|DV_I_50_2_bit|DV_II_46_2_bit));
+       if (mask & (DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit))
+               mask &= (((((W[35]>>4)^(W[39]>>29))&1)-1) | ~(DV_I_45_0_bit|DV_I_48_0_bit|DV_II_47_0_bit));
+       if (mask & (DV_I_48_0_bit|DV_II_48_0_bit))
+               mask &= ((0-((W[63]^(W[64]>>5))&(1<<0))) | ~(DV_I_48_0_bit|DV_II_48_0_bit));
+       if (mask & (DV_I_45_0_bit|DV_II_45_0_bit))
+               mask &= ((0-((W[63]^(W[64]>>5))&(1<<1))) | ~(DV_I_45_0_bit|DV_II_45_0_bit));
+       if (mask & (DV_I_47_0_bit|DV_II_47_0_bit))
+               mask &= ((0-((W[62]^(W[63]>>5))&(1<<0))) | ~(DV_I_47_0_bit|DV_II_47_0_bit));
+       if (mask & (DV_I_46_0_bit|DV_II_46_0_bit))
+               mask &= ((0-((W[61]^(W[62]>>5))&(1<<0))) | ~(DV_I_46_0_bit|DV_II_46_0_bit));
+       mask &= ((0-((W[61]^(W[62]>>5))&(1<<2))) | ~(DV_I_46_2_bit|DV_II_46_2_bit));
+       if (mask & (DV_I_45_0_bit|DV_II_45_0_bit))
+               mask &= ((0-((W[60]^(W[61]>>5))&(1<<0))) | ~(DV_I_45_0_bit|DV_II_45_0_bit));
+       if (mask & (DV_II_51_0_bit|DV_II_54_0_bit))
+               mask &= (((((W[58]^W[59])>>29)&1)-1) | ~(DV_II_51_0_bit|DV_II_54_0_bit));
+       if (mask & (DV_II_50_0_bit|DV_II_53_0_bit))
+               mask &= (((((W[57]^W[58])>>29)&1)-1) | ~(DV_II_50_0_bit|DV_II_53_0_bit));
+       if (mask & (DV_II_52_0_bit|DV_II_54_0_bit))
+               mask &= ((((W[56]^(W[59]>>25))&(1<<4))-(1<<4)) | ~(DV_II_52_0_bit|DV_II_54_0_bit));
+       if (mask & (DV_II_51_0_bit|DV_II_52_0_bit))
+               mask &= ((0-(((W[56]^W[59])>>29)&1)) | ~(DV_II_51_0_bit|DV_II_52_0_bit));
+       if (mask & (DV_II_49_0_bit|DV_II_52_0_bit))
+               mask &= (((((W[56]^W[57])>>29)&1)-1) | ~(DV_II_49_0_bit|DV_II_52_0_bit));
+       if (mask & (DV_II_51_0_bit|DV_II_53_0_bit))
+               mask &= ((((W[55]^(W[58]>>25))&(1<<4))-(1<<4)) | ~(DV_II_51_0_bit|DV_II_53_0_bit));
+       if (mask & (DV_II_50_0_bit|DV_II_52_0_bit))
+               mask &= ((((W[54]^(W[57]>>25))&(1<<4))-(1<<4)) | ~(DV_II_50_0_bit|DV_II_52_0_bit));
+       if (mask & (DV_II_49_0_bit|DV_II_51_0_bit))
+               mask &= ((((W[53]^(W[56]>>25))&(1<<4))-(1<<4)) | ~(DV_II_49_0_bit|DV_II_51_0_bit));
+       mask &= ((((W[51]^(W[50]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_46_2_bit));
+       mask &= ((((W[48]^W[50])&(1<<6))-(1<<6)) | ~(DV_I_50_2_bit|DV_II_46_2_bit));
+       if (mask & (DV_I_51_0_bit|DV_I_52_0_bit))
+               mask &= ((0-(((W[48]^W[55])>>29)&1)) | ~(DV_I_51_0_bit|DV_I_52_0_bit));
+       mask &= ((((W[47]^W[49])&(1<<6))-(1<<6)) | ~(DV_I_49_2_bit|DV_I_51_2_bit));
+       mask &= ((((W[48]^(W[47]>>5))&(1<<1))-(1<<1)) | ~(DV_I_47_2_bit|DV_II_51_2_bit));
+       mask &= ((((W[46]^W[48])&(1<<6))-(1<<6)) | ~(DV_I_48_2_bit|DV_I_50_2_bit));
+       mask &= ((((W[47]^(W[46]>>5))&(1<<1))-(1<<1)) | ~(DV_I_46_2_bit|DV_II_50_2_bit));
+       mask &= ((0-((W[44]^(W[45]>>5))&(1<<1))) | ~(DV_I_51_2_bit|DV_II_49_2_bit));
+       mask &= ((((W[43]^W[45])&(1<<6))-(1<<6)) | ~(DV_I_47_2_bit|DV_I_49_2_bit));
+       mask &= (((((W[42]^W[44])>>6)&1)-1) | ~(DV_I_46_2_bit|DV_I_48_2_bit));
+       mask &= ((((W[43]^(W[42]>>5))&(1<<1))-(1<<1)) | ~(DV_II_46_2_bit|DV_II_51_2_bit));
+       mask &= ((((W[42]^(W[41]>>5))&(1<<1))-(1<<1)) | ~(DV_I_51_2_bit|DV_II_50_2_bit));
+       mask &= ((((W[41]^(W[40]>>5))&(1<<1))-(1<<1)) | ~(DV_I_50_2_bit|DV_II_49_2_bit));
+       if (mask & (DV_I_52_0_bit|DV_II_51_0_bit))
+               mask &= ((((W[39]^(W[43]>>25))&(1<<4))-(1<<4)) | ~(DV_I_52_0_bit|DV_II_51_0_bit));
+       if (mask & (DV_I_51_0_bit|DV_II_50_0_bit))
+               mask &= ((((W[38]^(W[42]>>25))&(1<<4))-(1<<4)) | ~(DV_I_51_0_bit|DV_II_50_0_bit));
+       if (mask & (DV_I_48_2_bit|DV_I_51_2_bit))
+               mask &= ((0-((W[37]^(W[38]>>5))&(1<<1))) | ~(DV_I_48_2_bit|DV_I_51_2_bit));
+       if (mask & (DV_I_50_0_bit|DV_II_49_0_bit))
+               mask &= ((((W[37]^(W[41]>>25))&(1<<4))-(1<<4)) | ~(DV_I_50_0_bit|DV_II_49_0_bit));
+       if (mask & (DV_II_52_0_bit|DV_II_54_0_bit))
+               mask &= ((0-((W[36]^W[38])&(1<<4))) | ~(DV_II_52_0_bit|DV_II_54_0_bit));
+       mask &= ((0-((W[35]^(W[36]>>5))&(1<<1))) | ~(DV_I_46_2_bit|DV_I_49_2_bit));
+       if (mask & (DV_I_51_0_bit|DV_II_47_0_bit))
+               mask &= ((((W[35]^(W[39]>>25))&(1<<3))-(1<<3)) | ~(DV_I_51_0_bit|DV_II_47_0_bit));
+if (mask) {
+
+       if (mask & DV_I_43_0_bit)
+                if (
+                           !((W[61]^(W[62]>>5)) & (1<<1))
+                        || !(!((W[59]^(W[63]>>25)) & (1<<5)))
+                        || !((W[58]^(W[63]>>30)) & (1<<0))
+                )  mask &= ~DV_I_43_0_bit;
+       if (mask & DV_I_44_0_bit)
+                if (
+                           !((W[62]^(W[63]>>5)) & (1<<1))
+                        || !(!((W[60]^(W[64]>>25)) & (1<<5)))
+                        || !((W[59]^(W[64]>>30)) & (1<<0))
+                )  mask &= ~DV_I_44_0_bit;
+       if (mask & DV_I_46_2_bit)
+               mask &= ((~((W[40]^W[42])>>2)) | ~DV_I_46_2_bit);
+       if (mask & DV_I_47_2_bit)
+                if (
+                           !((W[62]^(W[63]>>5)) & (1<<2))
+                        || !(!((W[41]^W[43]) & (1<<6)))
+                )  mask &= ~DV_I_47_2_bit;
+       if (mask & DV_I_48_2_bit)
+                if (
+                           !((W[63]^(W[64]>>5)) & (1<<2))
+                        || !(!((W[48]^(W[49]<<5)) & (1<<6)))
+                )  mask &= ~DV_I_48_2_bit;
+       if (mask & DV_I_49_2_bit)
+                if (
+                           !(!((W[49]^(W[50]<<5)) & (1<<6)))
+                        || !((W[42]^W[50]) & (1<<1))
+                        || !(!((W[39]^(W[40]<<5)) & (1<<6)))
+                        || !((W[38]^W[40]) & (1<<1))
+                )  mask &= ~DV_I_49_2_bit;
+       if (mask & DV_I_50_0_bit)
+               mask &= ((((W[36]^W[37])<<7)) | ~DV_I_50_0_bit);
+       if (mask & DV_I_50_2_bit)
+               mask &= ((((W[43]^W[51])<<11)) | ~DV_I_50_2_bit);
+       if (mask & DV_I_51_0_bit)
+               mask &= ((((W[37]^W[38])<<9)) | ~DV_I_51_0_bit);
+       if (mask & DV_I_51_2_bit)
+                if (
+                           !(!((W[51]^(W[52]<<5)) & (1<<6)))
+                        || !(!((W[49]^W[51]) & (1<<6)))
+                        || !(!((W[37]^(W[37]>>5)) & (1<<1)))
+                        || !(!((W[35]^(W[39]>>25)) & (1<<5)))
+                )  mask &= ~DV_I_51_2_bit;
+       if (mask & DV_I_52_0_bit)
+               mask &= ((((W[38]^W[39])<<11)) | ~DV_I_52_0_bit);
+       if (mask & DV_II_46_2_bit)
+               mask &= ((((W[47]^W[51])<<17)) | ~DV_II_46_2_bit);
+       if (mask & DV_II_48_0_bit)
+                if (
+                           !(!((W[36]^(W[40]>>25)) & (1<<3)))
+                        || !((W[35]^(W[40]<<2)) & (1<<30))
+                )  mask &= ~DV_II_48_0_bit;
+       if (mask & DV_II_49_0_bit)
+                if (
+                           !(!((W[37]^(W[41]>>25)) & (1<<3)))
+                        || !((W[36]^(W[41]<<2)) & (1<<30))
+                )  mask &= ~DV_II_49_0_bit;
+       if (mask & DV_II_49_2_bit)
+                if (
+                           !(!((W[53]^(W[54]<<5)) & (1<<6)))
+                        || !(!((W[51]^W[53]) & (1<<6)))
+                        || !((W[50]^W[54]) & (1<<1))
+                        || !(!((W[45]^(W[46]<<5)) & (1<<6)))
+                        || !(!((W[37]^(W[41]>>25)) & (1<<5)))
+                        || !((W[36]^(W[41]>>30)) & (1<<0))
+                )  mask &= ~DV_II_49_2_bit;
+       if (mask & DV_II_50_0_bit)
+                if (
+                           !((W[55]^W[58]) & (1<<29))
+                        || !(!((W[38]^(W[42]>>25)) & (1<<3)))
+                        || !((W[37]^(W[42]<<2)) & (1<<30))
+                )  mask &= ~DV_II_50_0_bit;
+       if (mask & DV_II_50_2_bit)
+                if (
+                           !(!((W[54]^(W[55]<<5)) & (1<<6)))
+                        || !(!((W[52]^W[54]) & (1<<6)))
+                        || !((W[51]^W[55]) & (1<<1))
+                        || !((W[45]^W[47]) & (1<<1))
+                        || !(!((W[38]^(W[42]>>25)) & (1<<5)))
+                        || !((W[37]^(W[42]>>30)) & (1<<0))
+                )  mask &= ~DV_II_50_2_bit;
+       if (mask & DV_II_51_0_bit)
+                if (
+                           !(!((W[39]^(W[43]>>25)) & (1<<3)))
+                        || !((W[38]^(W[43]<<2)) & (1<<30))
+                )  mask &= ~DV_II_51_0_bit;
+       if (mask & DV_II_51_2_bit)
+                if (
+                           !(!((W[55]^(W[56]<<5)) & (1<<6)))
+                        || !(!((W[53]^W[55]) & (1<<6)))
+                        || !((W[52]^W[56]) & (1<<1))
+                        || !((W[46]^W[48]) & (1<<1))
+                        || !(!((W[39]^(W[43]>>25)) & (1<<5)))
+                        || !((W[38]^(W[43]>>30)) & (1<<0))
+                )  mask &= ~DV_II_51_2_bit;
+       if (mask & DV_II_52_0_bit)
+                if (
+                           !(!((W[59]^W[60]) & (1<<29)))
+                        || !(!((W[40]^(W[44]>>25)) & (1<<3)))
+                        || !(!((W[40]^(W[44]>>25)) & (1<<4)))
+                        || !((W[39]^(W[44]<<2)) & (1<<30))
+                )  mask &= ~DV_II_52_0_bit;
+       if (mask & DV_II_53_0_bit)
+                if (
+                           !((W[58]^W[61]) & (1<<29))
+                        || !(!((W[57]^(W[61]>>25)) & (1<<4)))
+                        || !(!((W[41]^(W[45]>>25)) & (1<<3)))
+                        || !(!((W[41]^(W[45]>>25)) & (1<<4)))
+                )  mask &= ~DV_II_53_0_bit;
+       if (mask & DV_II_54_0_bit)
+                if (
+                           !(!((W[58]^(W[62]>>25)) & (1<<4)))
+                        || !(!((W[42]^(W[46]>>25)) & (1<<3)))
+                        || !(!((W[42]^(W[46]>>25)) & (1<<4)))
+                )  mask &= ~DV_II_54_0_bit;
+       if (mask & DV_II_55_0_bit)
+                if (
+                           !(!((W[59]^(W[63]>>25)) & (1<<4)))
+                        || !(!((W[57]^(W[59]>>25)) & (1<<4)))
+                        || !(!((W[43]^(W[47]>>25)) & (1<<3)))
+                        || !(!((W[43]^(W[47]>>25)) & (1<<4)))
+                )  mask &= ~DV_II_55_0_bit;
+       if (mask & DV_II_56_0_bit)
+                if (
+                           !(!((W[60]^(W[64]>>25)) & (1<<4)))
+                        || !(!((W[44]^(W[48]>>25)) & (1<<3)))
+                        || !(!((W[44]^(W[48]>>25)) & (1<<4)))
+                )  mask &= ~DV_II_56_0_bit;
+}
+
+       dvmask[0]=mask;
+}
diff --git a/sha1dc/ubc_check.h b/sha1dc/ubc_check.h
new file mode 100644 (file)
index 0000000..b64c306
--- /dev/null
@@ -0,0 +1,44 @@
+/***
+* Copyright 2017 Marc Stevens <marc@marc-stevens.nl>, Dan Shumow <danshu@microsoft.com>
+* Distributed under the MIT Software License.
+* See accompanying file LICENSE.txt or copy at
+* https://opensource.org/licenses/MIT
+***/
+
+/*
+// this file was generated by the 'parse_bitrel' program in the tools section
+// using the data files from directory 'tools/data/3565'
+//
+// sha1_dvs contains a list of SHA-1 Disturbance Vectors (DV) to check
+// dvType, dvK and dvB define the DV: I(K,B) or II(K,B) (see the paper)
+// dm[80] is the expanded message block XOR-difference defined by the DV
+// testt is the step to do the recompression from for collision detection
+// maski and maskb define the bit to check for each DV in the dvmask returned by ubc_check
+//
+// ubc_check takes as input an expanded message block and verifies the unavoidable bitconditions for all listed DVs
+// it returns a dvmask where each bit belonging to a DV is set if all unavoidable bitconditions for that DV have been met
+// thus one needs to do the recompression check for each DV that has its bit set
+*/
+
+#ifndef UBC_CHECK_H
+#define UBC_CHECK_H
+
+#if defined(__cplusplus)
+extern "C" {
+#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[];
+void ubc_check(const uint32_t W[80], uint32_t dvmask[DVMASKSIZE]);
+
+#define DOSTORESTATE58
+#define DOSTORESTATE65
+
+#define CHECK_DVMASK(_DVMASK) (0 != _DVMASK[0])
+
+#if defined(__cplusplus)
+}
+#endif
+
+#endif /* UBC_CHECK_H */
index 11f7dde9d910093dce6a767150bb4823bff80200..25b6db989bf4ab475bd7fa224c63b753d61f586e 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -260,7 +260,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
 }
 
 static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
-                                  const struct sha1_array *extra,
+                                  const struct oid_array *extra,
                                   unsigned flags)
 {
        struct write_shallow_data data;
@@ -273,7 +273,7 @@ static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
        if (!extra)
                return data.count;
        for (i = 0; i < extra->nr; i++) {
-               strbuf_addstr(out, sha1_to_hex(extra->sha1[i]));
+               strbuf_addstr(out, oid_to_hex(extra->oid + i));
                strbuf_addch(out, '\n');
                data.count++;
        }
@@ -281,14 +281,14 @@ static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
 }
 
 int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
-                         const struct sha1_array *extra)
+                         const struct oid_array *extra)
 {
        return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
 }
 
 static struct tempfile temporary_shallow;
 
-const char *setup_temporary_shallow(const struct sha1_array *extra)
+const char *setup_temporary_shallow(const struct oid_array *extra)
 {
        struct strbuf sb = STRBUF_INIT;
        int fd;
@@ -312,7 +312,7 @@ const char *setup_temporary_shallow(const struct sha1_array *extra)
 
 void setup_alternate_shallow(struct lock_file *shallow_lock,
                             const char **alternate_shallow_file,
-                            const struct sha1_array *extra)
+                            const struct oid_array *extra)
 {
        struct strbuf sb = STRBUF_INIT;
        int fd;
@@ -385,7 +385,7 @@ struct trace_key trace_shallow = TRACE_KEY_INIT(SHALLOW);
  * Step 1, split sender shallow commits into "ours" and "theirs"
  * Step 2, clean "ours" based on .git/shallow
  */
-void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
+void prepare_shallow_info(struct shallow_info *info, struct oid_array *sa)
 {
        int i;
        trace_printf_key(&trace_shallow, "shallow: prepare_shallow_info\n");
@@ -396,9 +396,9 @@ void prepare_shallow_info(struct shallow_info *info, struct sha1_array *sa)
        ALLOC_ARRAY(info->ours, sa->nr);
        ALLOC_ARRAY(info->theirs, sa->nr);
        for (i = 0; i < sa->nr; i++) {
-               if (has_sha1_file(sa->sha1[i])) {
+               if (has_object_file(sa->oid + i)) {
                        struct commit_graft *graft;
-                       graft = lookup_commit_graft(sa->sha1[i]);
+                       graft = lookup_commit_graft(sa->oid[i].hash);
                        if (graft && graft->nr_parent < 0)
                                continue;
                        info->ours[info->nr_ours++] = i;
@@ -417,13 +417,13 @@ void clear_shallow_info(struct shallow_info *info)
 
 void remove_nonexistent_theirs_shallow(struct shallow_info *info)
 {
-       unsigned char (*sha1)[20] = info->shallow->sha1;
+       struct object_id *oid = info->shallow->oid;
        int i, dst;
        trace_printf_key(&trace_shallow, "shallow: remove_nonexistent_theirs_shallow\n");
        for (i = dst = 0; i < info->nr_theirs; i++) {
                if (i != dst)
                        info->theirs[dst] = info->theirs[i];
-               if (has_sha1_file(sha1[info->theirs[i]]))
+               if (has_object_file(oid + info->theirs[i]))
                        dst++;
        }
        info->nr_theirs = dst;
@@ -559,8 +559,8 @@ static void post_assign_shallow(struct shallow_info *info,
 void assign_shallow_commits_to_refs(struct shallow_info *info,
                                    uint32_t **used, int *ref_status)
 {
-       unsigned char (*sha1)[20] = info->shallow->sha1;
-       struct sha1_array *ref = info->ref;
+       struct object_id *oid = info->shallow->oid;
+       struct oid_array *ref = info->ref;
        unsigned int i, nr;
        int *shallow, nr_shallow = 0;
        struct paint_info pi;
@@ -599,18 +599,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(sha1[shallow[i]]);
+               struct commit *c = lookup_commit(oid[shallow[i]].hash);
                c->object.flags |= BOTTOM;
        }
 
        for (i = 0; i < ref->nr; i++)
-               paint_down(&pi, ref->sha1[i], i);
+               paint_down(&pi, ref->oid[i].hash, 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(sha1[shallow[i]]);
+                       const struct commit *c = lookup_commit(oid[shallow[i]].hash);
                        uint32_t **map = ref_bitmap_at(&pi.ref_bitmap, c);
                        if (*map)
                                used[shallow[i]] = xmemdupz(*map, bitmap_size);
@@ -664,7 +664,7 @@ static void post_assign_shallow(struct shallow_info *info,
                                struct ref_bitmap *ref_bitmap,
                                int *ref_status)
 {
-       unsigned char (*sha1)[20] = info->shallow->sha1;
+       struct object_id *oid = info->shallow->oid;
        struct commit *c;
        uint32_t **bitmap;
        int dst, i, j;
@@ -679,7 +679,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(sha1[info->theirs[i]]);
+               c = lookup_commit(oid[info->theirs[i]].hash);
                bitmap = ref_bitmap_at(ref_bitmap, c);
                if (!*bitmap)
                        continue;
@@ -700,7 +700,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(sha1[info->ours[i]]);
+               c = lookup_commit(oid[info->ours[i]].hash);
                bitmap = ref_bitmap_at(ref_bitmap, c);
                if (!*bitmap)
                        continue;
@@ -722,7 +722,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->sha1[c]);
+               struct commit *commit = lookup_commit(si->shallow->oid[c].hash);
 
                if (!si->commits) {
                        struct commit_array ca;
index ace58e7367300975e118660ed2004c7d0885481c..00457940cfc163fed58b0206f5167c25c2c71b8a 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -449,6 +449,17 @@ int strbuf_getcwd(struct strbuf *sb)
                        strbuf_setlen(sb, strlen(sb->buf));
                        return 0;
                }
+
+               /*
+                * If getcwd(3) is implemented as a syscall that falls
+                * back to a regular lookup using readdir(3) etc. then
+                * we may be able to avoid EACCES by providing enough
+                * space to the syscall as it's not necessarily bound
+                * to the same restrictions as the fallback.
+                */
+               if (errno == EACCES && guessed_len < PATH_MAX)
+                       continue;
+
                if (errno != ERANGE)
                        break;
        }
index 45016ad86dd5eb55534524cb50e30542e87fab70..003ca1879ef560b3db0ad9e1af8022c38800b500 100644 (file)
@@ -41,10 +41,7 @@ static int add_entry(int insert_at, struct string_list *list, const char *string
        if (exact_match)
                return -1 - index;
 
-       if (list->nr + 1 >= list->alloc) {
-               list->alloc += 32;
-               REALLOC_ARRAY(list->items, list->alloc);
-       }
+       ALLOC_GROW(list->items, list->nr+1, list->alloc);
        if (index < list->nr)
                memmove(list->items + index + 1, list->items + index,
                                (list->nr - index)
index 93453909cf3225e2b4b9630d4013f76987ca8be8..4f58491ddb0705ab56e238006199d35db19dfb5a 100644 (file)
@@ -234,6 +234,26 @@ int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg)
        return parse_fetch_recurse(opt, arg, 1);
 }
 
+static int parse_update_recurse(const char *opt, const char *arg,
+                               int die_on_error)
+{
+       switch (git_config_maybe_bool(opt, arg)) {
+       case 1:
+               return RECURSE_SUBMODULES_ON;
+       case 0:
+               return RECURSE_SUBMODULES_OFF;
+       default:
+               if (die_on_error)
+                       die("bad %s argument: %s", opt, arg);
+               return RECURSE_SUBMODULES_ERROR;
+       }
+}
+
+int parse_update_recurse_submodules_arg(const char *opt, const char *arg)
+{
+       return parse_update_recurse(opt, arg, 1);
+}
+
 static int parse_push_recurse(const char *opt, const char *arg,
                               int die_on_error)
 {
@@ -333,7 +353,7 @@ static int parse_config(const char *var, const char *value, void *data)
                         strcmp(value, "all") &&
                         strcmp(value, "none"))
                        warning("Invalid parameter '%s' for config option "
-                                       "'submodule.%s.ignore'", value, var);
+                                       "'submodule.%s.ignore'", value, name.buf);
                else {
                        free((void *) submodule->ignore);
                        submodule->ignore = xstrdup(value);
index 70f19363fd8bf5b7f42e974dacdc3ed2cd58a96a..d434ecdb45c074dd386405f20ed73ac1c8b646bd 100644 (file)
@@ -22,16 +22,17 @@ struct submodule {
        int recommend_shallow;
 };
 
-int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
-int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
-int parse_submodule_config_option(const char *var, const char *value);
-const struct submodule *submodule_from_name(const unsigned char *commit_or_tree,
-               const char *name);
-const struct submodule *submodule_from_path(const unsigned char *commit_or_tree,
-               const char *path);
+extern int parse_fetch_recurse_submodules_arg(const char *opt, const char *arg);
+extern int parse_update_recurse_submodules_arg(const char *opt, const char *arg);
+extern int parse_push_recurse_submodules_arg(const char *opt, const char *arg);
+extern int parse_submodule_config_option(const char *var, const char *value);
+extern const struct submodule *submodule_from_name(
+               const unsigned char *commit_or_tree, const char *name);
+extern const struct submodule *submodule_from_path(
+               const unsigned char *commit_or_tree, const char *path);
 extern int gitmodule_sha1_from_commit(const unsigned char *commit_sha1,
                                      unsigned char *gitmodules_sha1,
                                      struct strbuf *rev);
-void submodule_free(void);
+extern void submodule_free(void);
 
 #endif /* SUBMODULE_CONFIG_H */
index 3200b7bb2b2360c7efab86b680ed5d0cafa5e9d3..d3299e29c0855b8503f444f659e2b24bba8ecb1f 100644 (file)
 #include "blob.h"
 #include "thread-utils.h"
 #include "quote.h"
+#include "remote.h"
 #include "worktree.h"
 
 static int config_fetch_recurse_submodules = RECURSE_SUBMODULES_ON_DEMAND;
+static int config_update_recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int parallel_jobs = 1;
 static struct string_list changed_submodule_paths = STRING_LIST_INIT_NODUP;
 static int initialized_fetch_ref_tips;
-static struct sha1_array ref_tips_before_fetch;
-static struct sha1_array ref_tips_after_fetch;
+static struct oid_array ref_tips_before_fetch;
+static struct oid_array ref_tips_after_fetch;
 
 /*
  * The following flag is set if the .gitmodules file is unmerged. We then
@@ -212,37 +214,68 @@ void gitmodules_config_sha1(const unsigned char *commit_sha1)
 }
 
 /*
+ * NEEDSWORK: With the addition of different configuration options to determine
+ * if a submodule is of interests, the validity of this function's name comes
+ * into question.  Once the dust has settled and more concrete terminology is
+ * decided upon, come up with a more proper name for this function.  One
+ * potential candidate could be 'is_submodule_active()'.
+ *
  * Determine if a submodule has been initialized at a given 'path'
  */
 int is_submodule_initialized(const char *path)
 {
        int ret = 0;
-       const struct submodule *module = NULL;
+       char *key = NULL;
+       char *value = NULL;
+       const struct string_list *sl;
+       const struct submodule *module = submodule_from_path(null_sha1, path);
 
-       module = submodule_from_path(null_sha1, path);
+       /* early return if there isn't a path->module mapping */
+       if (!module)
+               return 0;
 
-       if (module) {
-               char *key = xstrfmt("submodule.%s.url", module->name);
-               char *value = NULL;
+       /* submodule.<name>.active is set */
+       key = xstrfmt("submodule.%s.active", module->name);
+       if (!git_config_get_bool(key, &ret)) {
+               free(key);
+               return ret;
+       }
+       free(key);
 
-               ret = !git_config_get_string(key, &value);
+       /* submodule.active is set */
+       sl = git_config_get_value_multi("submodule.active");
+       if (sl) {
+               struct pathspec ps;
+               struct argv_array args = ARGV_ARRAY_INIT;
+               const struct string_list_item *item;
 
-               free(value);
-               free(key);
+               for_each_string_list_item(item, sl) {
+                       argv_array_push(&args, item->string);
+               }
+
+               parse_pathspec(&ps, 0, 0, NULL, args.argv);
+               ret = match_pathspec(&ps, path, strlen(path), 0, NULL, 1);
+
+               argv_array_clear(&args);
+               clear_pathspec(&ps);
+               return ret;
        }
 
+       /* fallback to checking if the URL is set */
+       key = xstrfmt("submodule.%s.url", module->name);
+       ret = !git_config_get_string(key, &value);
+
+       free(value);
+       free(key);
        return ret;
 }
 
-/*
- * Determine if a submodule has been populated at a given 'path'
- */
-int is_submodule_populated(const char *path)
+int is_submodule_populated_gently(const char *path, int *return_error_code)
 {
        int ret = 0;
        char *gitdir = xstrfmt("%s/.git", path);
 
-       if (resolve_gitdir(gitdir))
+       if (resolve_gitdir_gently(gitdir, return_error_code))
                ret = 1;
 
        free(gitdir);
@@ -358,6 +391,23 @@ static void print_submodule_summary(struct rev_info *rev, FILE *f,
        strbuf_release(&sb);
 }
 
+static void prepare_submodule_repo_env_no_git_dir(struct argv_array *out)
+{
+       const char * const *var;
+
+       for (var = local_repo_env; *var; var++) {
+               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
+                       argv_array_push(out, *var);
+       }
+}
+
+void prepare_submodule_repo_env(struct argv_array *out)
+{
+       prepare_submodule_repo_env_no_git_dir(out);
+       argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
+                        DEFAULT_GIT_DIR_ENVIRONMENT);
+}
+
 /* Helper function to display the submodule header line prior to the full
  * summary output. If it can locate the submodule objects directory it will
  * attempt to lookup both the left and right commits and put them into the
@@ -527,6 +577,7 @@ void show_submodule_inline_diff(FILE *f, const char *path,
        if (!(dirty_submodule & DIRTY_SUBMODULE_MODIFIED))
                argv_array_push(&cp.args, oid_to_hex(new));
 
+       prepare_submodule_repo_env(&cp.env_array);
        if (run_command(&cp))
                fprintf(f, "(diff failed)\n");
 
@@ -545,41 +596,62 @@ 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;
+}
+
+const struct submodule *submodule_from_ce(const struct cache_entry *ce)
+{
+       if (!S_ISGITLINK(ce->ce_mode))
+               return NULL;
+
+       if (!should_update_submodules())
+               return NULL;
+
+       return submodule_from_path(null_sha1, ce->name);
+}
+
 static int has_remote(const char *refname, const struct object_id *oid,
                      int flags, void *cb_data)
 {
        return 1;
 }
 
-static int append_sha1_to_argv(const unsigned char sha1[20], void *data)
+static int append_oid_to_argv(const struct object_id *oid, void *data)
 {
        struct argv_array *argv = data;
-       argv_array_push(argv, sha1_to_hex(sha1));
+       argv_array_push(argv, oid_to_hex(oid));
        return 0;
 }
 
-static int check_has_commit(const unsigned char sha1[20], void *data)
+static int check_has_commit(const struct object_id *oid, void *data)
 {
        int *has_commit = data;
 
-       if (!lookup_commit_reference(sha1))
+       if (!lookup_commit_reference(oid->hash))
                *has_commit = 0;
 
        return 0;
 }
 
-static int submodule_has_commits(const char *path, struct sha1_array *commits)
+static int submodule_has_commits(const char *path, struct oid_array *commits)
 {
        int has_commit = 1;
 
        if (add_submodule_odb(path))
                return 0;
 
-       sha1_array_for_each_unique(commits, check_has_commit, &has_commit);
+       oid_array_for_each_unique(commits, check_has_commit, &has_commit);
        return has_commit;
 }
 
-static int submodule_needs_pushing(const char *path, struct sha1_array *commits)
+static int submodule_needs_pushing(const char *path, struct oid_array *commits)
 {
        if (!submodule_has_commits(path, commits))
                /*
@@ -601,7 +673,7 @@ static int submodule_needs_pushing(const char *path, struct sha1_array *commits)
                int needs_pushing = 0;
 
                argv_array_push(&cp.args, "rev-list");
-               sha1_array_for_each_unique(commits, append_sha1_to_argv, &cp.args);
+               oid_array_for_each_unique(commits, append_oid_to_argv, &cp.args);
                argv_array_pushl(&cp.args, "--not", "--remotes", "-n", "1" , NULL);
 
                prepare_submodule_repo_env(&cp.env_array);
@@ -623,18 +695,18 @@ static int submodule_needs_pushing(const char *path, struct sha1_array *commits)
        return 0;
 }
 
-static struct sha1_array *submodule_commits(struct string_list *submodules,
+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 sha1_array *) item->util;
+               return (struct oid_array *) item->util;
 
-       /* NEEDSWORK: should we have sha1_array_init()? */
-       item->util = xcalloc(1, sizeof(struct sha1_array));
-       return (struct sha1_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,
@@ -646,11 +718,11 @@ static void collect_submodules_from_diff(struct diff_queue_struct *q,
 
        for (i = 0; i < q->nr; i++) {
                struct diff_filepair *p = q->queue[i];
-               struct sha1_array *commits;
+               struct oid_array *commits;
                if (!S_ISGITLINK(p->two->mode))
                        continue;
                commits = submodule_commits(submodules, p->two->path);
-               sha1_array_append(commits, p->two->oid.hash);
+               oid_array_append(commits, &p->two->oid);
        }
 }
 
@@ -670,11 +742,11 @@ static void free_submodules_sha1s(struct string_list *submodules)
 {
        struct string_list_item *item;
        for_each_string_list_item(item, submodules)
-               sha1_array_clear((struct sha1_array *) item->util);
+               oid_array_clear((struct oid_array *) item->util);
        string_list_clear(submodules, 1);
 }
 
-int find_unpushed_submodules(struct sha1_array *commits,
+int find_unpushed_submodules(struct oid_array *commits,
                const char *remotes_name, struct string_list *needs_pushing)
 {
        struct rev_info rev;
@@ -687,7 +759,7 @@ int find_unpushed_submodules(struct sha1_array *commits,
 
        /* argv.argv[0] will be ignored by setup_revisions */
        argv_array_push(&argv, "find_unpushed_submodules");
-       sha1_array_for_each_unique(commits, append_sha1_to_argv, &argv);
+       oid_array_for_each_unique(commits, append_oid_to_argv, &argv);
        argv_array_push(&argv, "--not");
        argv_array_pushf(&argv, "--remotes=%s", remotes_name);
 
@@ -702,7 +774,7 @@ int find_unpushed_submodules(struct sha1_array *commits,
        argv_array_clear(&argv);
 
        for_each_string_list_item(submodule, &submodules) {
-               struct sha1_array *commits = (struct sha1_array *) submodule->util;
+               struct oid_array *commits = (struct oid_array *) submodule->util;
 
                if (submodule_needs_pushing(submodule->string, commits))
                        string_list_insert(needs_pushing, submodule->string);
@@ -712,7 +784,11 @@ int find_unpushed_submodules(struct sha1_array *commits,
        return needs_pushing->nr;
 }
 
-static int push_submodule(const char *path, int dry_run)
+static int push_submodule(const char *path,
+                         const struct remote *remote,
+                         const char **refspec, int refspec_nr,
+                         const struct string_list *push_options,
+                         int dry_run)
 {
        if (add_submodule_odb(path))
                return 1;
@@ -723,6 +799,20 @@ static int push_submodule(const char *path, int dry_run)
                if (dry_run)
                        argv_array_push(&cp.args, "--dry-run");
 
+               if (push_options && push_options->nr) {
+                       const struct string_list_item *item;
+                       for_each_string_list_item(item, push_options)
+                               argv_array_pushf(&cp.args, "--push-option=%s",
+                                                item->string);
+               }
+
+               if (remote->origin != REMOTE_UNCONFIGURED) {
+                       int i;
+                       argv_array_push(&cp.args, remote->name);
+                       for (i = 0; i < refspec_nr; i++)
+                               argv_array_push(&cp.args, refspec[i]);
+               }
+
                prepare_submodule_repo_env(&cp.env_array);
                cp.git_cmd = 1;
                cp.no_stdin = 1;
@@ -735,20 +825,67 @@ static int push_submodule(const char *path, int dry_run)
        return 1;
 }
 
-int push_unpushed_submodules(struct sha1_array *commits,
-                            const char *remotes_name,
+/*
+ * Perform a check in the submodule to see if the remote and refspec work.
+ * Die if the submodule can't be pushed.
+ */
+static void submodule_push_check(const char *path, const struct remote *remote,
+                                const char **refspec, int refspec_nr)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       int i;
+
+       argv_array_push(&cp.args, "submodule--helper");
+       argv_array_push(&cp.args, "push-check");
+       argv_array_push(&cp.args, remote->name);
+
+       for (i = 0; i < refspec_nr; i++)
+               argv_array_push(&cp.args, refspec[i]);
+
+       prepare_submodule_repo_env(&cp.env_array);
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.no_stdout = 1;
+       cp.dir = path;
+
+       /*
+        * Simply indicate if 'submodule--helper push-check' failed.
+        * More detailed error information will be provided by the
+        * child process.
+        */
+       if (run_command(&cp))
+               die("process for submodule '%s' failed", path);
+}
+
+int push_unpushed_submodules(struct oid_array *commits,
+                            const struct remote *remote,
+                            const char **refspec, int refspec_nr,
+                            const struct string_list *push_options,
                             int dry_run)
 {
        int i, ret = 1;
        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
-       if (!find_unpushed_submodules(commits, remotes_name, &needs_pushing))
+       if (!find_unpushed_submodules(commits, remote->name, &needs_pushing))
                return 1;
 
+       /*
+        * Verify that the remote and refspec can be propagated to all
+        * submodules.  This check can be skipped if the remote and refspec
+        * won't be propagated due to the remote being unconfigured (e.g. a URL
+        * instead of a remote name).
+        */
+       if (remote->origin != REMOTE_UNCONFIGURED)
+               for (i = 0; i < needs_pushing.nr; i++)
+                       submodule_push_check(needs_pushing.items[i].string,
+                                            remote, refspec, refspec_nr);
+
+       /* Actually push the submodules */
        for (i = 0; i < needs_pushing.nr; i++) {
                const char *path = needs_pushing.items[i].string;
                fprintf(stderr, "Pushing submodule '%s'\n", path);
-               if (!push_submodule(path, dry_run)) {
+               if (!push_submodule(path, remote, refspec, refspec_nr,
+                                   push_options, dry_run)) {
                        fprintf(stderr, "Unable to push submodule '%s'\n", path);
                        ret = 0;
                }
@@ -817,23 +954,23 @@ static void submodule_collect_changed_cb(struct diff_queue_struct *q,
 static int add_sha1_to_array(const char *ref, const struct object_id *oid,
                             int flags, void *data)
 {
-       sha1_array_append(data, oid->hash);
+       oid_array_append(data, oid);
        return 0;
 }
 
-void check_for_new_submodule_commits(unsigned char new_sha1[20])
+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);
                initialized_fetch_ref_tips = 1;
        }
 
-       sha1_array_append(&ref_tips_after_fetch, new_sha1);
+       oid_array_append(&ref_tips_after_fetch, oid);
 }
 
-static int add_sha1_to_argv(const unsigned char sha1[20], void *data)
+static int add_oid_to_argv(const struct object_id *oid, void *data)
 {
-       argv_array_push(data, sha1_to_hex(sha1));
+       argv_array_push(data, oid_to_hex(oid));
        return 0;
 }
 
@@ -849,11 +986,11 @@ static void calculate_changed_submodule_paths(void)
 
        init_revisions(&rev, NULL);
        argv_array_push(&argv, "--"); /* argv[0] program name */
-       sha1_array_for_each_unique(&ref_tips_after_fetch,
-                                  add_sha1_to_argv, &argv);
+       oid_array_for_each_unique(&ref_tips_after_fetch,
+                                  add_oid_to_argv, &argv);
        argv_array_push(&argv, "--not");
-       sha1_array_for_each_unique(&ref_tips_before_fetch,
-                                  add_sha1_to_argv, &argv);
+       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");
@@ -879,8 +1016,8 @@ static void calculate_changed_submodule_paths(void)
        }
 
        argv_array_clear(&argv);
-       sha1_array_clear(&ref_tips_before_fetch);
-       sha1_array_clear(&ref_tips_after_fetch);
+       oid_array_clear(&ref_tips_before_fetch);
+       oid_array_clear(&ref_tips_after_fetch);
        initialized_fetch_ref_tips = 0;
 }
 
@@ -1041,67 +1178,78 @@ int fetch_populated_submodules(const struct argv_array *options,
 
 unsigned is_submodule_modified(const char *path, int ignore_untracked)
 {
-       ssize_t len;
        struct child_process cp = CHILD_PROCESS_INIT;
-       const char *argv[] = {
-               "status",
-               "--porcelain",
-               NULL,
-               NULL,
-       };
        struct strbuf buf = STRBUF_INIT;
+       FILE *fp;
        unsigned dirty_submodule = 0;
-       const char *line, *next_line;
        const char *git_dir;
+       int ignore_cp_exit_code = 0;
 
        strbuf_addf(&buf, "%s/.git", path);
        git_dir = read_gitfile(buf.buf);
        if (!git_dir)
                git_dir = buf.buf;
-       if (!is_directory(git_dir)) {
+       if (!is_git_directory(git_dir)) {
+               if (is_directory(git_dir))
+                       die(_("'%s' not recognized as a git repository"), git_dir);
                strbuf_release(&buf);
                /* The submodule is not checked out, so it is not modified */
                return 0;
-
        }
        strbuf_reset(&buf);
 
+       argv_array_pushl(&cp.args, "status", "--porcelain=2", NULL);
        if (ignore_untracked)
-               argv[2] = "-uno";
+               argv_array_push(&cp.args, "-uno");
 
-       cp.argv = argv;
        prepare_submodule_repo_env(&cp.env_array);
        cp.git_cmd = 1;
        cp.no_stdin = 1;
        cp.out = -1;
        cp.dir = path;
        if (start_command(&cp))
-               die("Could not run 'git status --porcelain' in submodule %s", path);
+               die("Could not run 'git status --porcelain=2' in submodule %s", path);
 
-       len = strbuf_read(&buf, cp.out, 1024);
-       line = buf.buf;
-       while (len > 2) {
-               if ((line[0] == '?') && (line[1] == '?')) {
+       fp = xfdopen(cp.out, "r");
+       while (strbuf_getwholeline(&buf, fp, '\n') != EOF) {
+               /* regular untracked files */
+               if (buf.buf[0] == '?')
                        dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
-                       if (dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
-                               break;
-               } else {
-                       dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
-                       if (ignore_untracked ||
-                           (dirty_submodule & DIRTY_SUBMODULE_UNTRACKED))
-                               break;
+
+               if (buf.buf[0] == 'u' ||
+                   buf.buf[0] == '1' ||
+                   buf.buf[0] == '2') {
+                       /* T = line type, XY = status, SSSS = submodule state */
+                       if (buf.len < strlen("T XY SSSS"))
+                               die("BUG: invalid status --porcelain=2 line %s",
+                                   buf.buf);
+
+                       if (buf.buf[5] == 'S' && buf.buf[8] == 'U')
+                               /* nested untracked file */
+                               dirty_submodule |= DIRTY_SUBMODULE_UNTRACKED;
+
+                       if (buf.buf[0] == 'u' ||
+                           buf.buf[0] == '2' ||
+                           memcmp(buf.buf + 5, "S..U", 4))
+                               /* other change */
+                               dirty_submodule |= DIRTY_SUBMODULE_MODIFIED;
                }
-               next_line = strchr(line, '\n');
-               if (!next_line)
+
+               if ((dirty_submodule & DIRTY_SUBMODULE_MODIFIED) &&
+                   ((dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ||
+                    ignore_untracked)) {
+                       /*
+                        * We're not interested in any further information from
+                        * the child any more, neither output nor its exit code.
+                        */
+                       ignore_cp_exit_code = 1;
                        break;
-               next_line++;
-               len -= (next_line - line);
-               line = next_line;
+               }
        }
-       close(cp.out);
+       fclose(fp);
 
-       if (finish_command(&cp))
-               die("'git status --porcelain' failed in submodule %s", path);
+       if (finish_command(&cp) && !ignore_cp_exit_code)
+               die("'git status --porcelain=2' failed in submodule %s", path);
 
        strbuf_release(&buf);
        return dirty_submodule;
@@ -1181,7 +1329,7 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
        cp.dir = path;
        if (start_command(&cp)) {
                if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR)
-                       die(_("could not start 'git status in submodule '%s'"),
+                       die(_("could not start 'git status' in submodule '%s'"),
                                path);
                ret = -1;
                goto out;
@@ -1194,7 +1342,7 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
 
        if (finish_command(&cp)) {
                if (flags & SUBMODULE_REMOVAL_DIE_ON_ERROR)
-                       die(_("could not run 'git status in submodule '%s'"),
+                       die(_("could not run 'git status' in submodule '%s'"),
                                path);
                ret = -1;
        }
@@ -1203,6 +1351,150 @@ int bad_to_remove_submodule(const char *path, unsigned flags)
        return ret;
 }
 
+static const char *get_super_prefix_or_empty(void)
+{
+       const char *s = get_super_prefix();
+       if (!s)
+               s = "";
+       return s;
+}
+
+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);
+
+       cp.git_cmd = 1;
+       argv_array_pushl(&cp.args, "diff-index", "--quiet",
+                                  "--cached", "HEAD", NULL);
+       cp.no_stdin = 1;
+       cp.no_stdout = 1;
+       cp.dir = sub->path;
+       if (start_command(&cp))
+               die("could not recurse into submodule '%s'", sub->path);
+
+       return finish_command(&cp);
+}
+
+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);
+
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.dir = path;
+
+       argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+                                  get_super_prefix_or_empty(), path);
+       argv_array_pushl(&cp.args, "read-tree", "-u", "--reset", NULL);
+
+       argv_array_push(&cp.args, EMPTY_TREE_SHA1_HEX);
+
+       if (run_command(&cp))
+               die("could not reset submodule index");
+}
+
+/**
+ * Moves a submodule at a given path from a given head to another new head.
+ * For edge cases (a submodule coming into existence or removing a submodule)
+ * pass NULL for old or new respectively.
+ */
+int submodule_move_head(const char *path,
+                        const char *old,
+                        const char *new,
+                        unsigned flags)
+{
+       int ret = 0;
+       struct child_process cp = CHILD_PROCESS_INIT;
+       const struct submodule *sub;
+
+       sub = submodule_from_path(null_sha1, path);
+
+       if (!sub)
+               die("BUG: could not get submodule information for '%s'", path);
+
+       if (old && !(flags & SUBMODULE_MOVE_HEAD_FORCE)) {
+               /* Check if the submodule has a dirty index. */
+               if (submodule_has_dirty_index(sub))
+                       return error(_("submodule '%s' has dirty index"), path);
+       }
+
+       if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
+               if (old) {
+                       if (!submodule_uses_gitfile(path))
+                               absorb_git_dir_into_superproject("", path,
+                                       ABSORB_GITDIR_RECURSE_SUBMODULES);
+               } else {
+                       struct strbuf sb = STRBUF_INIT;
+                       strbuf_addf(&sb, "%s/modules/%s",
+                                   get_git_common_dir(), sub->name);
+                       connect_work_tree_and_git_dir(path, sb.buf);
+                       strbuf_release(&sb);
+
+                       /* make sure the index is clean as well */
+                       submodule_reset_index(path);
+               }
+       }
+
+       prepare_submodule_repo_env_no_git_dir(&cp.env_array);
+
+       cp.git_cmd = 1;
+       cp.no_stdin = 1;
+       cp.dir = path;
+
+       argv_array_pushf(&cp.args, "--super-prefix=%s%s/",
+                       get_super_prefix_or_empty(), path);
+       argv_array_pushl(&cp.args, "read-tree", NULL);
+
+       if (flags & SUBMODULE_MOVE_HEAD_DRY_RUN)
+               argv_array_push(&cp.args, "-n");
+       else
+               argv_array_push(&cp.args, "-u");
+
+       if (flags & SUBMODULE_MOVE_HEAD_FORCE)
+               argv_array_push(&cp.args, "--reset");
+       else
+               argv_array_push(&cp.args, "-m");
+
+       argv_array_push(&cp.args, old ? old : EMPTY_TREE_SHA1_HEX);
+       argv_array_push(&cp.args, new ? new : EMPTY_TREE_SHA1_HEX);
+
+       if (run_command(&cp)) {
+               ret = -1;
+               goto out;
+       }
+
+       if (!(flags & SUBMODULE_MOVE_HEAD_DRY_RUN)) {
+               if (new) {
+                       struct child_process cp1 = CHILD_PROCESS_INIT;
+                       /* also set the HEAD accordingly */
+                       cp1.git_cmd = 1;
+                       cp1.no_stdin = 1;
+                       cp1.dir = path;
+
+                       argv_array_pushl(&cp1.args, "update-ref", "HEAD", new, NULL);
+
+                       if (run_command(&cp1)) {
+                               ret = -1;
+                               goto out;
+                       }
+               } else {
+                       struct strbuf sb = STRBUF_INIT;
+
+                       strbuf_addf(&sb, "%s/.git", path);
+                       unlink_or_warn(sb.buf);
+                       strbuf_release(&sb);
+
+                       if (is_empty_dir(path))
+                               rmdir_or_warn(path);
+               }
+       }
+out:
+       return ret;
+}
+
 static int find_first_merges(struct object_array *result, const char *path,
                struct commit *a, struct commit *b)
 {
@@ -1221,7 +1513,7 @@ static int find_first_merges(struct object_array *result, const char *path,
        memset(&rev_opts, 0, sizeof(rev_opts));
 
        /* get all revisions that merge commit a */
-       snprintf(merged_revision, sizeof(merged_revision), "^%s",
+       xsnprintf(merged_revision, sizeof(merged_revision), "^%s",
                        oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
@@ -1371,18 +1663,6 @@ int parallel_submodules(void)
        return parallel_jobs;
 }
 
-void prepare_submodule_repo_env(struct argv_array *out)
-{
-       const char * const *var;
-
-       for (var = local_repo_env; *var; var++) {
-               if (strcmp(*var, CONFIG_DATA_ENVIRONMENT))
-                       argv_array_push(out, *var);
-       }
-       argv_array_pushf(out, "%s=%s", GIT_DIR_ENVIRONMENT,
-                        DEFAULT_GIT_DIR_ENVIRONMENT);
-}
-
 /*
  * Embeds a single submodules git directory into the superprojects git dir,
  * non recursively.
@@ -1414,11 +1694,8 @@ static void relocate_single_git_dir_into_superproject(const char *prefix,
                die(_("could not create directory '%s'"), new_git_dir);
        real_new_git_dir = real_pathdup(new_git_dir, 1);
 
-       if (!prefix)
-               prefix = get_super_prefix();
-
        fprintf(stderr, _("Migrating git directory of '%s%s' from\n'%s' to\n'%s'\n"),
-               prefix ? prefix : "", path,
+               get_super_prefix_or_empty(), path,
                real_old_git_dir, real_new_git_dir);
 
        relocate_gitdir(path, real_old_git_dir, real_new_git_dir);
@@ -1445,8 +1722,6 @@ void absorb_git_dir_into_superproject(const char *prefix,
 
        /* Not populated? */
        if (!sub_git_dir) {
-               char *real_new_git_dir;
-               const char *new_git_dir;
                const struct submodule *sub;
 
                if (err_code == READ_GITFILE_ERR_STAT_FAILED) {
@@ -1469,13 +1744,8 @@ void absorb_git_dir_into_superproject(const char *prefix,
                sub = submodule_from_path(null_sha1, path);
                if (!sub)
                        die(_("could not lookup name for submodule '%s'"), path);
-               new_git_dir = git_path("modules/%s", sub->name);
-               if (safe_create_leading_directories_const(new_git_dir) < 0)
-                       die(_("could not create directory '%s'"), new_git_dir);
-               real_new_git_dir = real_pathdup(new_git_dir, 1);
-               connect_work_tree_and_git_dir(path, real_new_git_dir);
-
-               free(real_new_git_dir);
+               connect_work_tree_and_git_dir(path,
+                       git_path("modules/%s", sub->name));
        } else {
                /* Is it already absorbed into the superprojects git dir? */
                char *real_sub_git_dir = real_pathdup(sub_git_dir, 1);
@@ -1496,8 +1766,7 @@ void absorb_git_dir_into_superproject(const char *prefix,
                if (flags & ~ABSORB_GITDIR_RECURSE_SUBMODULES)
                        die("BUG: we don't know how to pass the flags down?");
 
-               if (get_super_prefix())
-                       strbuf_addstr(&sb, get_super_prefix());
+               strbuf_addstr(&sb, get_super_prefix_or_empty());
                strbuf_addstr(&sb, path);
                strbuf_addch(&sb, '/');
 
@@ -1596,3 +1865,34 @@ const char *get_superproject_working_tree(void)
 
        return ret;
 }
+
+int submodule_to_gitdir(struct strbuf *buf, const char *submodule)
+{
+       const struct submodule *sub;
+       const char *git_dir;
+       int ret = 0;
+
+       strbuf_reset(buf);
+       strbuf_addstr(buf, submodule);
+       strbuf_complete(buf, '/');
+       strbuf_addstr(buf, ".git");
+
+       git_dir = read_gitfile(buf->buf);
+       if (git_dir) {
+               strbuf_reset(buf);
+               strbuf_addstr(buf, git_dir);
+       }
+       if (!is_git_directory(buf->buf)) {
+               gitmodules_config();
+               sub = submodule_from_path(null_sha1, submodule);
+               if (!sub) {
+                       ret = -1;
+                       goto cleanup;
+               }
+               strbuf_reset(buf);
+               strbuf_git_path(buf, "%s/%s", "modules", sub->name);
+       }
+
+cleanup:
+       return ret;
+}
index c8a0c9cb2971219b807f7fe931c4dc5355b978ea..1277480add48140c6bf8c6cbc51cb962e27637ae 100644 (file)
@@ -3,7 +3,8 @@
 
 struct diff_options;
 struct argv_array;
-struct sha1_array;
+struct oid_array;
+struct remote;
 
 enum {
        RECURSE_SUBMODULES_ONLY = -5,
@@ -41,7 +42,13 @@ extern int submodule_config(const char *var, const char *value, void *cb);
 extern void gitmodules_config(void);
 extern void gitmodules_config_sha1(const unsigned char *commit_sha1);
 extern int is_submodule_initialized(const char *path);
-extern int is_submodule_populated(const char *path);
+/*
+ * Determine if a submodule has been populated at a given 'path' by checking if
+ * the <path>/.git resolves to a valid git repository.
+ * If return_error_code is NULL, die on error.
+ * 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 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);
@@ -58,7 +65,15 @@ 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 check_for_new_submodule_commits(unsigned char new_sha1[20]);
+extern void set_config_update_recurse_submodules(int value);
+/* Check if we want to update any submodule.*/
+extern int should_update_submodules(void);
+/*
+ * Returns the submodule struct if the given ce entry is a submodule
+ * and it should be updated. Returns NULL otherwise.
+ */
+extern const struct submodule *submodule_from_ce(const struct cache_entry *ce);
+extern void check_for_new_submodule_commits(struct object_id *oid);
 extern int fetch_populated_submodules(const struct argv_array *options,
                               const char *prefix, int command_line_option,
                               int quiet, int max_parallel_jobs);
@@ -73,14 +88,29 @@ 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 find_unpushed_submodules(struct sha1_array *commits,
+extern int find_unpushed_submodules(struct oid_array *commits,
                                    const char *remotes_name,
                                    struct string_list *needs_pushing);
-extern int push_unpushed_submodules(struct sha1_array *commits,
-                                   const char *remotes_name,
+extern int push_unpushed_submodules(struct oid_array *commits,
+                                   const struct remote *remote,
+                                   const char **refspec, int refspec_nr,
+                                   const struct string_list *push_options,
                                    int dry_run);
 extern void connect_work_tree_and_git_dir(const char *work_tree, const char *git_dir);
 extern int parallel_submodules(void);
+/*
+ * Given a submodule path (as in the index), return the repository
+ * path of that submodule in 'buf'. Return -1 on error or when the
+ * submodule is not initialized.
+ */
+int submodule_to_gitdir(struct strbuf *buf, const char *submodule);
+
+#define SUBMODULE_MOVE_HEAD_DRY_RUN (1<<0)
+#define SUBMODULE_MOVE_HEAD_FORCE   (1<<1)
+extern int submodule_move_head(const char *path,
+                              const char *old,
+                              const char *new,
+                              unsigned flags);
 
 /*
  * Prepare the "env_array" parameter of a "struct child_process" for executing
index 4982d1c5216c10b7d44e66eace34e40d49cb5b2f..ab386c36818890a085d8917102b8f8a479ff34cd 100644 (file)
--- a/t/README
+++ b/t/README
@@ -471,13 +471,13 @@ Don't:
    their output.
 
    You can glean some further possible issues from the TAP grammar
-   (see http://search.cpan.org/perldoc?TAP::Parser::Grammar#TAP_Grammar)
+   (see https://metacpan.org/pod/TAP::Parser::Grammar#TAP-GRAMMAR)
    but the best indication is to just run the tests with prove(1),
    it'll complain if anything is amiss.
 
 Keep in mind:
 
- - Inside <script> part, the standard output and standard error
+ - Inside the <script> part, the standard output and standard error
    streams are discarded, and the test harness only reports "ok" or
    "not ok" to the end user running the tests. Under --verbose, they
    are shown to help debugging the tests.
@@ -611,9 +611,11 @@ library for your script to use.
 
  - test_have_prereq <prereq>
 
-   Check if we have a prerequisite previously set with
-   test_set_prereq. The most common use of this directly is to skip
-   all the tests if we don't have some essential prerequisite:
+   Check if we have a prerequisite previously set with test_set_prereq.
+   The most common way to use this explicitly (as opposed to the
+   implicit use when an argument is passed to test_expect_*) is to skip
+   all the tests at the start of the test script if we don't have some
+   essential prerequisite:
 
        if ! test_have_prereq PERL
        then
index d6e8b3679872ab1e8d2e7493947e09c842c4140a..acd5db180f31f5b81877c75f8a695c3e4437e34d 100644 (file)
 /test-genrandom
 /test-hashmap
 /test-index-version
+/test-lazy-init-name-hash
 /test-line-buffer
 /test-match-trees
 /test-mergesort
 /test-mktemp
+/test-online-cpus
 /test-parse-options
 /test-path-utils
 /test-prio-queue
 /test-read-cache
+/test-ref-store
 /test-regex
 /test-revision-walking
 /test-run-command
index 83a4f2ab86999876ecfb78c7e6dd108eb09b04ae..8e3ed6a76cb97831e85152af8e9da21d18ed2fc1 100644 (file)
@@ -66,6 +66,16 @@ static int iterate_cb(const char *var, const char *value, void *data)
        return 0;
 }
 
+static int early_config_cb(const char *var, const char *value, void *vdata)
+{
+       const char *key = vdata;
+
+       if (!strcmp(key, var))
+               printf("%s\n", value);
+
+       return 0;
+}
+
 int cmd_main(int argc, const char **argv)
 {
        int i, val;
@@ -73,6 +83,11 @@ int cmd_main(int argc, const char **argv)
        const struct string_list *strptr;
        struct config_set cs;
 
+       if (argc == 3 && !strcmp(argv[1], "read_early_config")) {
+               read_early_config(early_config_cb, (void *)argv[2]);
+               return 0;
+       }
+
        setup_git_directory();
 
        git_configset_init(&cs);
diff --git a/t/helper/test-lazy-init-name-hash.c b/t/helper/test-lazy-init-name-hash.c
new file mode 100644 (file)
index 0000000..6368a89
--- /dev/null
@@ -0,0 +1,264 @@
+#include "cache.h"
+#include "parse-options.h"
+
+static int single;
+static int multi;
+static int count = 1;
+static int dump;
+static int perf;
+static int analyze;
+static int analyze_step;
+
+/*
+ * Dump the contents of the "dir" and "name" hash tables to stdout.
+ * If you sort the result, you can compare it with the other type
+ * mode and verify that both single and multi produce the same set.
+ */
+static void dump_run(void)
+{
+       struct hashmap_iter iter_dir;
+       struct hashmap_iter iter_cache;
+
+       /* Stolen from name-hash.c */
+       struct dir_entry {
+               struct hashmap_entry ent;
+               struct dir_entry *parent;
+               int nr;
+               unsigned int namelen;
+               char name[FLEX_ARRAY];
+       };
+
+       struct dir_entry *dir;
+       struct cache_entry *ce;
+
+       read_cache();
+       if (single) {
+               test_lazy_init_name_hash(&the_index, 0);
+       } else {
+               int nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
+               if (!nr_threads_used)
+                       die("non-threaded code path used");
+       }
+
+       dir = hashmap_iter_first(&the_index.dir_hash, &iter_dir);
+       while (dir) {
+               printf("dir %08x %7d %s\n", dir->ent.hash, dir->nr, dir->name);
+               dir = hashmap_iter_next(&iter_dir);
+       }
+
+       ce = hashmap_iter_first(&the_index.name_hash, &iter_cache);
+       while (ce) {
+               printf("name %08x %s\n", ce->ent.hash, ce->name);
+               ce = hashmap_iter_next(&iter_cache);
+       }
+
+       discard_cache();
+}
+
+/*
+ * Run the single or multi threaded version "count" times and
+ * report on the time taken.
+ */
+static uint64_t time_runs(int try_threaded)
+{
+       uint64_t t0, t1, t2;
+       uint64_t sum = 0;
+       uint64_t avg;
+       int nr_threads_used;
+       int i;
+
+       for (i = 0; i < count; i++) {
+               t0 = getnanotime();
+               read_cache();
+               t1 = getnanotime();
+               nr_threads_used = test_lazy_init_name_hash(&the_index, try_threaded);
+               t2 = getnanotime();
+
+               sum += (t2 - t1);
+
+               if (try_threaded && !nr_threads_used)
+                       die("non-threaded code path used");
+
+               if (nr_threads_used)
+                       printf("%f %f %d multi %d\n",
+                                  ((double)(t1 - t0))/1000000000,
+                                  ((double)(t2 - t1))/1000000000,
+                                  the_index.cache_nr,
+                                  nr_threads_used);
+               else
+                       printf("%f %f %d single\n",
+                                  ((double)(t1 - t0))/1000000000,
+                                  ((double)(t2 - t1))/1000000000,
+                                  the_index.cache_nr);
+               fflush(stdout);
+
+               discard_cache();
+       }
+
+       avg = sum / count;
+       if (count > 1)
+               printf("avg %f %s\n",
+                          (double)avg/1000000000,
+                          (try_threaded) ? "multi" : "single");
+
+       return avg;
+}
+
+/*
+ * Try a series of runs varying the "istate->cache_nr" and
+ * try to find a good value for the multi-threaded criteria.
+ */
+static void analyze_run(void)
+{
+       uint64_t t1s, t1m, t2s, t2m;
+       int cache_nr_limit;
+       int nr_threads_used;
+       int i;
+       int nr;
+
+       read_cache();
+       cache_nr_limit = the_index.cache_nr;
+       discard_cache();
+
+       nr = analyze;
+       while (1) {
+               uint64_t sum_single = 0;
+               uint64_t sum_multi = 0;
+               uint64_t avg_single;
+               uint64_t avg_multi;
+
+               if (nr > cache_nr_limit)
+                       nr = cache_nr_limit;
+
+               for (i = 0; i < count; i++) {
+                       read_cache();
+                       the_index.cache_nr = nr; /* cheap truncate of index */
+                       t1s = getnanotime();
+                       test_lazy_init_name_hash(&the_index, 0);
+                       t2s = getnanotime();
+                       sum_single += (t2s - t1s);
+                       the_index.cache_nr = cache_nr_limit;
+                       discard_cache();
+
+                       read_cache();
+                       the_index.cache_nr = nr; /* cheap truncate of index */
+                       t1m = getnanotime();
+                       nr_threads_used = test_lazy_init_name_hash(&the_index, 1);
+                       t2m = getnanotime();
+                       sum_multi += (t2m - t1m);
+                       the_index.cache_nr = cache_nr_limit;
+                       discard_cache();
+
+                       if (!nr_threads_used)
+                               printf("    [size %8d] [single %f]   non-threaded code path used\n",
+                                          nr, ((double)(t2s - t1s))/1000000000);
+                       else
+                               printf("    [size %8d] [single %f] %c [multi %f %d]\n",
+                                          nr,
+                                          ((double)(t2s - t1s))/1000000000,
+                                          (((t2s - t1s) < (t2m - t1m)) ? '<' : '>'),
+                                          ((double)(t2m - t1m))/1000000000,
+                                          nr_threads_used);
+                       fflush(stdout);
+               }
+               if (count > 1) {
+                       avg_single = sum_single / count;
+                       avg_multi = sum_multi / count;
+                       if (!nr_threads_used)
+                               printf("avg [size %8d] [single %f]\n",
+                                          nr,
+                                          (double)avg_single/1000000000);
+                       else
+                               printf("avg [size %8d] [single %f] %c [multi %f %d]\n",
+                                          nr,
+                                          (double)avg_single/1000000000,
+                                          (avg_single < avg_multi ? '<' : '>'),
+                                          (double)avg_multi/1000000000,
+                                          nr_threads_used);
+                       fflush(stdout);
+               }
+
+               if (nr >= cache_nr_limit)
+                       return;
+               nr += analyze_step;
+       }
+}
+
+int cmd_main(int argc, const char **argv)
+{
+       const char *usage[] = {
+               "test-lazy-init-name-hash -d (-s | -m)",
+               "test-lazy-init-name-hash -p [-c c]",
+               "test-lazy-init-name-hash -a a [--step s] [-c c]",
+               "test-lazy-init-name-hash (-s | -m) [-c c]",
+               "test-lazy-init-name-hash -s -m [-c c]",
+               NULL
+       };
+       struct option options[] = {
+               OPT_BOOL('s', "single", &single, "run single-threaded code"),
+               OPT_BOOL('m', "multi", &multi, "run multi-threaded code"),
+               OPT_INTEGER('c', "count", &count, "number of passes"),
+               OPT_BOOL('d', "dump", &dump, "dump hash tables"),
+               OPT_BOOL('p', "perf", &perf, "compare single vs multi"),
+               OPT_INTEGER('a', "analyze", &analyze, "analyze different multi sizes"),
+               OPT_INTEGER(0, "step", &analyze_step, "analyze step factor"),
+               OPT_END(),
+       };
+       const char *prefix;
+       uint64_t avg_single, avg_multi;
+
+       prefix = setup_git_directory();
+
+       argc = parse_options(argc, argv, prefix, options, usage, 0);
+
+       /*
+        * istate->dir_hash is only created when ignore_case is set.
+        */
+       ignore_case = 1;
+
+       if (dump) {
+               if (perf || analyze > 0)
+                       die("cannot combine dump, perf, or analyze");
+               if (count > 1)
+                       die("count not valid with dump");
+               if (single && multi)
+                       die("cannot use both single and multi with dump");
+               if (!single && !multi)
+                       die("dump requires either single or multi");
+               dump_run();
+               return 0;
+       }
+
+       if (perf) {
+               if (analyze > 0)
+                       die("cannot combine dump, perf, or analyze");
+               if (single || multi)
+                       die("cannot use single or multi with perf");
+               avg_single = time_runs(0);
+               avg_multi = time_runs(1);
+               if (avg_multi > avg_single)
+                       die("multi is slower");
+               return 0;
+       }
+
+       if (analyze) {
+               if (analyze < 500)
+                       die("analyze must be at least 500");
+               if (!analyze_step)
+                       analyze_step = analyze;
+               if (single || multi)
+                       die("cannot use single or multi with analyze");
+               analyze_run();
+               return 0;
+       }
+
+       if (!single && !multi)
+               die("require either -s or -m or both");
+
+       if (single)
+               time_runs(0);
+       if (multi)
+               time_runs(1);
+
+       return 0;
+}
diff --git a/t/helper/test-online-cpus.c b/t/helper/test-online-cpus.c
new file mode 100644 (file)
index 0000000..06c09c6
--- /dev/null
@@ -0,0 +1,8 @@
+#include "git-compat-util.h"
+#include "thread-utils.h"
+
+int cmd_main(int argc, const char **argv)
+{
+       printf("%d\n", online_cpus());
+       return 0;
+}
index 2a7990efc31d042121122a17890c623d7714c128..48255eef31a1e83d9a2bbb01670ba089a11c91f8 100644 (file)
@@ -5,6 +5,7 @@ int cmd_main(int argc, const char **argv)
        int i, cnt = 1;
        if (argc == 2)
                cnt = strtol(argv[1], NULL, 0);
+       setup_git_directory();
        for (i = 0; i < cnt; i++) {
                read_cache();
                discard_cache();
diff --git a/t/helper/test-ref-store.c b/t/helper/test-ref-store.c
new file mode 100644 (file)
index 0000000..2d84c45
--- /dev/null
@@ -0,0 +1,277 @@
+#include "cache.h"
+#include "refs.h"
+
+static const char *notnull(const char *arg, const char *name)
+{
+       if (!arg)
+               die("%s required", name);
+       return arg;
+}
+
+static unsigned int arg_flags(const char *arg, const char *name)
+{
+       return atoi(notnull(arg, name));
+}
+
+static const char **get_store(const char **argv, struct ref_store **refs)
+{
+       const char *gitdir;
+
+       if (!argv[0]) {
+               die("ref store required");
+       } else if (!strcmp(argv[0], "main")) {
+               *refs = get_main_ref_store();
+       } else if (skip_prefix(argv[0], "submodule:", &gitdir)) {
+               struct strbuf sb = STRBUF_INIT;
+               int ret;
+
+               ret = strbuf_git_path_submodule(&sb, gitdir, "objects/");
+               if (ret)
+                       die("strbuf_git_path_submodule failed: %d", ret);
+               add_to_alternates_memory(sb.buf);
+               strbuf_release(&sb);
+
+               *refs = get_submodule_ref_store(gitdir);
+       } else
+               die("unknown backend %s", argv[0]);
+
+       if (!*refs)
+               die("no ref store");
+
+       /* consume store-specific optional arguments if needed */
+
+       return argv + 1;
+}
+
+
+static int cmd_pack_refs(struct ref_store *refs, const char **argv)
+{
+       unsigned int flags = arg_flags(*argv++, "flags");
+
+       return refs_pack_refs(refs, flags);
+}
+
+static int cmd_peel_ref(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+       unsigned char sha1[20];
+       int ret;
+
+       ret = refs_peel_ref(refs, refname, sha1);
+       if (!ret)
+               puts(sha1_to_hex(sha1));
+       return ret;
+}
+
+static int cmd_create_symref(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+       const char *target = notnull(*argv++, "target");
+       const char *logmsg = *argv++;
+
+       return refs_create_symref(refs, refname, target, logmsg);
+}
+
+static int cmd_delete_refs(struct ref_store *refs, const char **argv)
+{
+       unsigned int flags = arg_flags(*argv++, "flags");
+       struct string_list refnames = STRING_LIST_INIT_NODUP;
+
+       while (*argv)
+               string_list_append(&refnames, *argv++);
+
+       return refs_delete_refs(refs, &refnames, flags);
+}
+
+static int cmd_rename_ref(struct ref_store *refs, const char **argv)
+{
+       const char *oldref = notnull(*argv++, "oldref");
+       const char *newref = notnull(*argv++, "newref");
+       const char *logmsg = *argv++;
+
+       return refs_rename_ref(refs, oldref, newref, logmsg);
+}
+
+static int each_ref(const char *refname, const struct object_id *oid,
+                   int flags, void *cb_data)
+{
+       printf("%s %s 0x%x\n", oid_to_hex(oid), refname, flags);
+       return 0;
+}
+
+static int cmd_for_each_ref(struct ref_store *refs, const char **argv)
+{
+       const char *prefix = notnull(*argv++, "prefix");
+
+       return refs_for_each_ref_in(refs, prefix, each_ref, NULL);
+}
+
+static int cmd_resolve_ref(struct ref_store *refs, const char **argv)
+{
+       unsigned char sha1[20];
+       const char *refname = notnull(*argv++, "refname");
+       int resolve_flags = arg_flags(*argv++, "resolve-flags");
+       int flags;
+       const char *ref;
+
+       ref = refs_resolve_ref_unsafe(refs, refname, resolve_flags,
+                                     sha1, &flags);
+       printf("%s %s 0x%x\n", sha1_to_hex(sha1), ref, flags);
+       return ref ? 0 : 1;
+}
+
+static int cmd_verify_ref(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+       struct strbuf err = STRBUF_INIT;
+       int ret;
+
+       ret = refs_verify_refname_available(refs, refname, NULL, NULL, &err);
+       if (err.len)
+               puts(err.buf);
+       return ret;
+}
+
+static int cmd_for_each_reflog(struct ref_store *refs, const char **argv)
+{
+       return refs_for_each_reflog(refs, each_ref, NULL);
+}
+
+static int each_reflog(struct object_id *old_oid, struct object_id *new_oid,
+                      const char *committer, unsigned long timestamp,
+                      int tz, const char *msg, void *cb_data)
+{
+       printf("%s %s %s %lu %d %s\n",
+              oid_to_hex(old_oid), oid_to_hex(new_oid),
+              committer, timestamp, tz, msg);
+       return 0;
+}
+
+static int cmd_for_each_reflog_ent(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+
+       return refs_for_each_reflog_ent(refs, refname, each_reflog, refs);
+}
+
+static int cmd_for_each_reflog_ent_reverse(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+
+       return refs_for_each_reflog_ent_reverse(refs, refname, each_reflog, refs);
+}
+
+static int cmd_reflog_exists(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+
+       return !refs_reflog_exists(refs, refname);
+}
+
+static int cmd_create_reflog(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+       int force_create = arg_flags(*argv++, "force-create");
+       struct strbuf err = STRBUF_INIT;
+       int ret;
+
+       ret = refs_create_reflog(refs, refname, force_create, &err);
+       if (err.len)
+               puts(err.buf);
+       return ret;
+}
+
+static int cmd_delete_reflog(struct ref_store *refs, const char **argv)
+{
+       const char *refname = notnull(*argv++, "refname");
+
+       return refs_delete_reflog(refs, refname);
+}
+
+static int cmd_reflog_expire(struct ref_store *refs, const char **argv)
+{
+       die("not supported yet");
+}
+
+static int cmd_delete_ref(struct ref_store *refs, const char **argv)
+{
+       const char *msg = notnull(*argv++, "msg");
+       const char *refname = notnull(*argv++, "refname");
+       const char *sha1_buf = notnull(*argv++, "old-sha1");
+       unsigned int flags = arg_flags(*argv++, "flags");
+       unsigned char old_sha1[20];
+
+       if (get_sha1_hex(sha1_buf, old_sha1))
+               die("not sha-1");
+
+       return refs_delete_ref(refs, msg, refname, old_sha1, flags);
+}
+
+static int cmd_update_ref(struct ref_store *refs, const char **argv)
+{
+       const char *msg = notnull(*argv++, "msg");
+       const char *refname = notnull(*argv++, "refname");
+       const char *new_sha1_buf = notnull(*argv++, "old-sha1");
+       const char *old_sha1_buf = notnull(*argv++, "old-sha1");
+       unsigned int flags = arg_flags(*argv++, "flags");
+       unsigned char old_sha1[20];
+       unsigned char new_sha1[20];
+
+       if (get_sha1_hex(old_sha1_buf, old_sha1) ||
+           get_sha1_hex(new_sha1_buf, new_sha1))
+               die("not sha-1");
+
+       return refs_update_ref(refs, msg, refname,
+                              new_sha1, old_sha1,
+                              flags, UPDATE_REFS_DIE_ON_ERR);
+}
+
+struct command {
+       const char *name;
+       int (*func)(struct ref_store *refs, const char **argv);
+};
+
+static struct command commands[] = {
+       { "pack-refs", cmd_pack_refs },
+       { "peel-ref", cmd_peel_ref },
+       { "create-symref", cmd_create_symref },
+       { "delete-refs", cmd_delete_refs },
+       { "rename-ref", cmd_rename_ref },
+       { "for-each-ref", cmd_for_each_ref },
+       { "resolve-ref", cmd_resolve_ref },
+       { "verify-ref", cmd_verify_ref },
+       { "for-each-reflog", cmd_for_each_reflog },
+       { "for-each-reflog-ent", cmd_for_each_reflog_ent },
+       { "for-each-reflog-ent-reverse", cmd_for_each_reflog_ent_reverse },
+       { "reflog-exists", cmd_reflog_exists },
+       { "create-reflog", cmd_create_reflog },
+       { "delete-reflog", cmd_delete_reflog },
+       { "reflog-expire", cmd_reflog_expire },
+       /*
+        * backend transaction functions can't be tested separately
+        */
+       { "delete-ref", cmd_delete_ref },
+       { "update-ref", cmd_update_ref },
+       { NULL, NULL }
+};
+
+int cmd_main(int argc, const char **argv)
+{
+       struct ref_store *refs;
+       const char *func;
+       struct command *cmd;
+
+       setup_git_directory();
+
+       argv = get_store(argv + 1, &refs);
+
+       func = *argv++;
+       if (!func)
+               die("ref function required");
+       for (cmd = commands; cmd->name; cmd++) {
+               if (!strcmp(func, cmd->name))
+                       return cmd->func(refs, argv);
+       }
+       die("unknown function %s", func);
+       return 0;
+}
index f7a53c4ad64c18903f53c5bd1d4766f54bc22e49..edfd52d82aeca0bb9ba2c5b2ce18bc39d27e5a34 100644 (file)
@@ -1,33 +1,33 @@
 #include "cache.h"
 #include "sha1-array.h"
 
-static int print_sha1(const unsigned char sha1[20], void *data)
+static int print_oid(const struct object_id *oid, void *data)
 {
-       puts(sha1_to_hex(sha1));
+       puts(oid_to_hex(oid));
        return 0;
 }
 
 int cmd_main(int argc, const char **argv)
 {
-       struct sha1_array array = SHA1_ARRAY_INIT;
+       struct oid_array array = OID_ARRAY_INIT;
        struct strbuf line = STRBUF_INIT;
 
        while (strbuf_getline(&line, stdin) != EOF) {
                const char *arg;
-               unsigned char sha1[20];
+               struct object_id oid;
 
                if (skip_prefix(line.buf, "append ", &arg)) {
-                       if (get_sha1_hex(arg, sha1))
+                       if (get_oid_hex(arg, &oid))
                                die("not a hexadecimal SHA1: %s", arg);
-                       sha1_array_append(&array, sha1);
+                       oid_array_append(&array, &oid);
                } else if (skip_prefix(line.buf, "lookup ", &arg)) {
-                       if (get_sha1_hex(arg, sha1))
+                       if (get_oid_hex(arg, &oid))
                                die("not a hexadecimal SHA1: %s", arg);
-                       printf("%d\n", sha1_array_lookup(&array, sha1));
+                       printf("%d\n", oid_array_lookup(&array, &oid));
                } else if (!strcmp(line.buf, "clear"))
-                       sha1_array_clear(&array);
+                       oid_array_clear(&array);
                else if (!strcmp(line.buf, "for_each_unique"))
-                       sha1_array_for_each_unique(&array, print_sha1, NULL);
+                       oid_array_for_each_unique(&array, print_oid, NULL);
                else
                        die("unknown command: %s", line.buf);
        }
index 915eb4a7c65ca5c574beddc676bf0115990eec3f..fb4f7b014e10cd383ed42550f77a14ea9b0211ac 100755 (executable)
@@ -4,6 +4,7 @@
 # - New submodule (no_submodule => add_sub1)
 # - Removed submodule (add_sub1 => remove_sub1)
 # - Updated submodule (add_sub1 => modify_sub1)
+# - Updated submodule recursively (add_nested_sub => modify_sub1_recursively)
 # - Submodule updated to invalid commit (add_sub1 => invalid_sub1)
 # - Submodule updated from invalid commit (invalid_sub1 => valid_sub1)
 # - Submodule replaced by tracked files in directory (add_sub1 =>
 # - Tracked file replaced by submodule (replace_sub1_with_file =>
 #   replace_file_with_sub1)
 #
-#                   --O-----O
-#                  /  ^     replace_directory_with_sub1
-#                 /   replace_sub1_with_directory
-#                /----O
-#               /     ^
-#              /      modify_sub1
-#      O------O-------O
-#      ^      ^\      ^
-#      |      | \     remove_sub1
-#      |      |  -----O-----O
-#      |      |   \   ^     replace_file_with_sub1
-#      |      |    \  replace_sub1_with_file
-#      |   add_sub1 --O-----O
-# no_submodule        ^     valid_sub1
-#                     invalid_sub1
+#                     ----O
+#                    /    ^
+#                   /     remove_sub1
+#                  /
+#       add_sub1  /-------O---------O--------O  modify_sub1_recursively
+#             |  /        ^         add_nested_sub
+#             | /         modify_sub1
+#             v/
+#      O------O-----------O---------O
+#      ^       \          ^         replace_directory_with_sub1
+#      |        \         replace_sub1_with_directory
+# no_submodule   \
+#                 --------O---------O
+#                  \      ^         replace_file_with_sub1
+#                   \     replace_sub1_with_file
+#                    \
+#                     ----O---------O
+#                         ^         valid_sub1
+#                         invalid_sub1
 #
+
 create_lib_submodule_repo () {
+       git init submodule_update_sub1 &&
+       (
+               cd submodule_update_sub1 &&
+               echo "expect" >>.gitignore &&
+               echo "actual" >>.gitignore &&
+               echo "x" >file1 &&
+               echo "y" >file2 &&
+               git add .gitignore file1 file2 &&
+               git commit -m "Base inside first submodule" &&
+               git branch "no_submodule"
+       ) &&
+       git init submodule_update_sub2 &&
+       (
+               cd submodule_update_sub2
+               echo "expect" >>.gitignore &&
+               echo "actual" >>.gitignore &&
+               echo "x" >file1 &&
+               echo "y" >file2 &&
+               git add .gitignore file1 file2 &&
+               git commit -m "nested submodule base" &&
+               git branch "no_submodule"
+       ) &&
        git init submodule_update_repo &&
        (
                cd submodule_update_repo &&
@@ -44,15 +72,16 @@ create_lib_submodule_repo () {
                git branch "no_submodule" &&
 
                git checkout -b "add_sub1" &&
-               git submodule add ./. sub1 &&
+               git submodule add ../submodule_update_sub1 sub1 &&
                git config -f .gitmodules submodule.sub1.ignore all &&
                git config submodule.sub1.ignore all &&
                git add .gitmodules &&
                git commit -m "Add sub1" &&
-               git checkout -b remove_sub1 &&
+
+               git checkout -b remove_sub1 add_sub1 &&
                git revert HEAD &&
 
-               git checkout -b "modify_sub1" "add_sub1" &&
+               git checkout -b modify_sub1 add_sub1 &&
                git submodule update &&
                (
                        cd sub1 &&
@@ -67,7 +96,27 @@ create_lib_submodule_repo () {
                git add sub1 &&
                git commit -m "Modify sub1" &&
 
-               git checkout -b "replace_sub1_with_directory" "add_sub1" &&
+               git checkout -b add_nested_sub modify_sub1 &&
+               git -C sub1 checkout -b "add_nested_sub" &&
+               git -C sub1 submodule add --branch no_submodule ../submodule_update_sub2 sub2 &&
+               git -C sub1 commit -a -m "add a nested submodule" &&
+               git add sub1 &&
+               git commit -a -m "update submodule, that updates a nested submodule" &&
+               git checkout -b modify_sub1_recursively &&
+               git -C sub1 checkout -b modify_sub1_recursively &&
+               git -C sub1/sub2 checkout -b modify_sub1_recursively &&
+               echo change >sub1/sub2/file3 &&
+               git -C sub1/sub2 add file3 &&
+               git -C sub1/sub2 commit -m "make a change in nested sub" &&
+               git -C sub1 add sub2 &&
+               git -C sub1 commit -m "update nested sub" &&
+               git add sub1 &&
+               git commit -m "update sub1, that updates nested sub" &&
+               git -C sub1 push origin modify_sub1_recursively &&
+               git -C sub1/sub2 push origin modify_sub1_recursively &&
+               git -C sub1 submodule deinit -f --all &&
+
+               git checkout -b replace_sub1_with_directory add_sub1 &&
                git submodule update &&
                git -C sub1 checkout modifications &&
                git rm --cached sub1 &&
@@ -75,22 +124,25 @@ create_lib_submodule_repo () {
                git config -f .gitmodules --remove-section "submodule.sub1" &&
                git add .gitmodules sub1/* &&
                git commit -m "Replace sub1 with directory" &&
+
                git checkout -b replace_directory_with_sub1 &&
                git revert HEAD &&
 
-               git checkout -b "replace_sub1_with_file" "add_sub1" &&
+               git checkout -b replace_sub1_with_file add_sub1 &&
                git rm sub1 &&
                echo "content" >sub1 &&
                git add sub1 &&
                git commit -m "Replace sub1 with file" &&
+
                git checkout -b replace_file_with_sub1 &&
                git revert HEAD &&
 
-               git checkout -b "invalid_sub1" "add_sub1" &&
+               git checkout -b invalid_sub1 add_sub1 &&
                git update-index --cacheinfo 160000 0123456789012345678901234567890123456789 sub1 &&
                git commit -m "Invalid sub1 commit" &&
                git checkout -b valid_sub1 &&
                git revert HEAD &&
+
                git checkout master
        )
 }
@@ -130,6 +182,15 @@ test_git_directory_is_unchanged () {
        )
 }
 
+test_git_directory_exists() {
+       test -e ".git/modules/$1" &&
+       if test -f sub1/.git
+       then
+               # does core.worktree point at the right place?
+               test "$(git -C .git/modules/$1 config core.worktree)" = "../../../$1"
+       fi
+}
+
 # Helper function to be executed at the start of every test below, it sets up
 # the submodule repo if it doesn't exist and configures the most problematic
 # settings for diff.ignoreSubmodules.
@@ -151,15 +212,36 @@ reset_work_tree_to () {
                git checkout -f "$1" &&
                git status -u -s >actual &&
                test_must_be_empty actual &&
-               sha1=$(git rev-parse --revs-only HEAD:sub1) &&
-               if test -n "$sha1" &&
-                  test $(cd "sub1" && git rev-parse --verify "$sha1^{commit}")
+               hash=$(git rev-parse --revs-only HEAD:sub1) &&
+               if test -n "$hash" &&
+                  test $(cd "../submodule_update_sub1" && git rev-parse --verify "$hash^{commit}")
                then
                        git submodule update --init --recursive "sub1"
                fi
        )
 }
 
+reset_work_tree_to_interested () {
+       reset_work_tree_to $1 &&
+       # make the submodule git dirs available
+       if ! test -d submodule_update/.git/modules/sub1
+       then
+               mkdir -p submodule_update/.git/modules &&
+               cp -r submodule_update_repo/.git/modules/sub1 submodule_update/.git/modules/sub1
+               GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1 config --unset core.worktree
+       fi &&
+       if ! test -d submodule_update/.git/modules/sub1/modules/sub2
+       then
+               mkdir -p submodule_update/.git/modules/sub1/modules &&
+               cp -r submodule_update_repo/.git/modules/sub1/modules/sub2 submodule_update/.git/modules/sub1/modules/sub2
+               GIT_WORK_TREE=. git -C submodule_update/.git/modules/sub1/modules/sub2 config --unset core.worktree
+       fi &&
+       # indicate we are interested in the submodule:
+       git -C submodule_update config submodule.sub1.url "bogus" &&
+       # sub1 might not be checked out, so use the git dir
+       git -C submodule_update/.git/modules/sub1 config submodule.sub2.url "bogus"
+}
+
 # Test that the superproject contains the content according to commit "$1"
 # (the work tree must match the index for everything but submodules but the
 # index must exactly match the given commit including any submodule SHA-1s).
@@ -173,6 +255,11 @@ test_superproject_content () {
 # Test that the given submodule at path "$1" contains the content according
 # to the submodule commit recorded in the superproject's commit "$2"
 test_submodule_content () {
+       if test x"$1" = "x-C"
+       then
+               cd "$2"
+               shift; shift;
+       fi
        if test $# != 2
        then
                echo "test_submodule_content needs two arguments"
@@ -675,3 +762,464 @@ test_submodule_forced_switch () {
                )
        '
 }
+
+# Test that submodule contents are correctly updated when switching
+# between commits that change a submodule.
+# Test that the following transitions are correctly handled:
+# (These tests are also above in the case where we expect no change
+#  in the submodule)
+# - Updated submodule
+# - New submodule
+# - Removed submodule
+# - Directory containing tracked files replaced by submodule
+# - Submodule replaced by tracked files in directory
+# - Submodule replaced by tracked file with the same name
+# - tracked file replaced by submodule
+#
+# New test cases
+# - Removing a submodule with a git directory absorbs the submodules
+#   git directory first into the superproject.
+
+test_submodule_switch_recursing () {
+       command="$1"
+       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
+               RESULTOI=failure
+       fi
+       ######################### Appearing submodule #########################
+       # Switching to a commit letting a submodule appear checks it out ...
+       test_expect_success "$command: added submodule is checked out" '
+               prolog &&
+               reset_work_tree_to_interested no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... ignoring an empty existing directory ...
+       test_expect_success "$command: added submodule is checked out in empty dir" '
+               prolog &&
+               reset_work_tree_to_interested no_submodule &&
+               (
+                       cd submodule_update &&
+                       mkdir sub1 &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... unless there is an untracked file in its place.
+       test_expect_success "$command: added submodule doesn't remove untracked file with same name" '
+               prolog &&
+               reset_work_tree_to_interested no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       : >sub1 &&
+                       test_must_fail $command add_sub1 &&
+                       test_superproject_content origin/no_submodule &&
+                       test_must_be_empty sub1
+               )
+       '
+       # ... but an ignored file is fine.
+       test_expect_$RESULTOI "$command: added submodule removes an untracked ignored file" '
+               test_when_finished "rm submodule_update/.git/info/exclude" &&
+               prolog &&
+               reset_work_tree_to_interested no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       : >sub1 &&
+                       echo sub1 >.git/info/exclude
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Replacing a tracked file with a submodule produces a checked out submodule
+       test_expect_success "$command: replace tracked file with submodule checks out submodule" '
+               prolog &&
+               reset_work_tree_to_interested replace_sub1_with_file &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+                       $command replace_file_with_sub1 &&
+                       test_superproject_content origin/replace_file_with_sub1 &&
+                       test_submodule_content sub1 origin/replace_file_with_sub1
+               )
+       '
+       # ... as does removing a directory with tracked files with a submodule.
+       test_expect_success "$command: replace directory with submodule" '
+               prolog &&
+               reset_work_tree_to_interested replace_sub1_with_directory &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+                       $command replace_directory_with_sub1 &&
+                       test_superproject_content origin/replace_directory_with_sub1 &&
+                       test_submodule_content sub1 origin/replace_directory_with_sub1
+               )
+       '
+
+       ######################## Disappearing submodule #######################
+       # Removing a submodule removes its work tree ...
+       test_expect_success "$command: removed submodule removes submodules working tree" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       ! test -e sub1
+               )
+       '
+       # ... absorbing a .git directory along the way.
+       test_expect_success "$command: removed submodule absorbs submodules .git directory" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       rm -rf .git/modules &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       ! test -e sub1 &&
+                       test_git_directory_exists sub1
+               )
+       '
+       # Replacing a submodule with files in a directory must succeeds
+       # when the submodule is clean
+       test_expect_$RESULTDS "$command: replace submodule with a directory" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       $command replace_sub1_with_directory &&
+                       test_superproject_content origin/replace_sub1_with_directory &&
+                       test_submodule_content sub1 origin/replace_sub1_with_directory
+               )
+       '
+       # ... absorbing a .git directory.
+       test_expect_$RESULTDS "$command: replace submodule containing a .git directory with a directory must absorb the git dir" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       rm -rf .git/modules &&
+                       $command replace_sub1_with_directory &&
+                       test_superproject_content origin/replace_sub1_with_directory &&
+                       test_git_directory_exists sub1
+               )
+       '
+
+       # Replacing it with a file ...
+       test_expect_success "$command: replace submodule with a file" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       $command replace_sub1_with_file &&
+                       test_superproject_content origin/replace_sub1_with_file &&
+                       test -f sub1
+               )
+       '
+
+       # ... must check its local work tree for untracked files
+       test_expect_$RESULTDS "$command: replace submodule with a file must fail with untracked files" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       : >sub1/untrackedfile &&
+                       test_must_fail $command replace_sub1_with_file &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+
+       # ... and ignored files are ignored
+       test_expect_success "$command: replace submodule with a file works ignores ignored files in submodule" '
+               test_when_finished "rm submodule_update/.git/modules/sub1/info/exclude" &&
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       : >sub1/ignored &&
+                       $command replace_sub1_with_file &&
+                       test_superproject_content origin/replace_sub1_with_file &&
+                       test -f sub1
+               )
+       '
+
+       ########################## Modified submodule #########################
+       # Updating a submodule sha1 updates the submodule's work tree
+       test_expect_success "$command: 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 &&
+                       $command 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" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t invalid_sub1 origin/invalid_sub1 &&
+                       test_must_fail $command invalid_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+
+       # recursing deeper than one level doesn't work yet.
+       test_expect_$RESULTR "$command: modified submodule updates submodule recursively" '
+               prolog &&
+               reset_work_tree_to_interested add_nested_sub &&
+               (
+                       cd submodule_update &&
+                       git branch -t modify_sub1_recursively origin/modify_sub1_recursively &&
+                       $command modify_sub1_recursively &&
+                       test_superproject_content origin/modify_sub1_recursively &&
+                       test_submodule_content sub1 origin/modify_sub1_recursively &&
+                       test_submodule_content -C sub1 sub2 origin/modify_sub1_recursively
+               )
+       '
+}
+
+# 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"
+       RESULT=success
+       if test "$KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS" = 1
+       then
+               RESULT=failure
+       fi
+       ######################### Appearing submodule #########################
+       # Switching to a commit letting a submodule appear creates empty dir ...
+       test_expect_success "$command: added submodule is checked out" '
+               prolog &&
+               reset_work_tree_to_interested no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... and doesn't care if it already exists ...
+       test_expect_success "$command: added submodule ignores empty directory" '
+               prolog &&
+               reset_work_tree_to_interested no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       mkdir sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # ... not caring about an untracked file either
+       test_expect_success "$command: added submodule does remove untracked unignored file with same name when forced" '
+               prolog &&
+               reset_work_tree_to_interested no_submodule &&
+               (
+                       cd submodule_update &&
+                       git branch -t add_sub1 origin/add_sub1 &&
+                       >sub1 &&
+                       $command add_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Replacing a tracked file with a submodule checks out the submodule
+       test_expect_success "$command: replace tracked file with submodule populates the submodule" '
+               prolog &&
+               reset_work_tree_to_interested replace_sub1_with_file &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_file_with_sub1 origin/replace_file_with_sub1 &&
+                       $command replace_file_with_sub1 &&
+                       test_superproject_content origin/replace_file_with_sub1 &&
+                       test_submodule_content sub1 origin/replace_file_with_sub1
+               )
+       '
+       # ... as does removing a directory with tracked files with a
+       # submodule.
+       test_expect_success "$command: replace directory with submodule" '
+               prolog &&
+               reset_work_tree_to_interested replace_sub1_with_directory &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_directory_with_sub1 origin/replace_directory_with_sub1 &&
+                       $command replace_directory_with_sub1 &&
+                       test_superproject_content origin/replace_directory_with_sub1 &&
+                       test_submodule_content sub1 origin/replace_directory_with_sub1
+               )
+       '
+
+       ######################## Disappearing submodule #######################
+       # Removing a submodule doesn't remove its work tree ...
+       test_expect_success "$command: removed submodule leaves submodule directory and its contents in place" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       ! test -e sub1
+               )
+       '
+       # ... especially when it contains a .git directory.
+       test_expect_success "$command: removed submodule leaves submodule containing a .git directory alone" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t remove_sub1 origin/remove_sub1 &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       rm -rf .git/modules/sub1 &&
+                       $command remove_sub1 &&
+                       test_superproject_content origin/remove_sub1 &&
+                       test_git_directory_exists sub1 &&
+                       ! test -e sub1
+               )
+       '
+       # Replacing a submodule with files in a directory ...
+       test_expect_success "$command: replace submodule with a directory" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       $command replace_sub1_with_directory &&
+                       test_superproject_content origin/replace_sub1_with_directory
+               )
+       '
+       # ... absorbing a .git directory.
+       test_expect_success "$command: replace submodule containing a .git directory with a directory must fail" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_directory origin/replace_sub1_with_directory &&
+                       replace_gitfile_with_git_dir sub1 &&
+                       rm -rf .git/modules/sub1 &&
+                       $command replace_sub1_with_directory &&
+                       test_superproject_content origin/replace_sub1_with_directory &&
+                       test_submodule_content sub1 origin/modify_sub1
+                       test_git_directory_exists sub1
+               )
+       '
+       # Replacing it with a file
+       test_expect_success "$command: replace submodule with a file" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       $command replace_sub1_with_file &&
+                       test_superproject_content origin/replace_sub1_with_file
+               )
+       '
+
+       # ... even if the submodule contains ignored files
+       test_expect_success "$command: replace submodule with a file ignoring ignored files" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       : >sub1/expect &&
+                       $command replace_sub1_with_file &&
+                       test_superproject_content origin/replace_sub1_with_file
+               )
+       '
+
+       # ... but stops for untracked files that would be lost
+       test_expect_$RESULT "$command: replace submodule with a file stops for untracked files" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t replace_sub1_with_file origin/replace_sub1_with_file &&
+                       : >sub1/untracked_file &&
+                       test_must_fail $command replace_sub1_with_file &&
+                       test_superproject_content origin/add_sub1 &&
+                       test -f sub1/untracked_file
+               )
+       '
+
+       ########################## Modified submodule #########################
+       # Updating a submodule sha1 updates the submodule's work tree
+       test_expect_success "$command: 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 &&
+                       $command 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
+       # submodule's work tree, subsequent update will fail
+       test_expect_success "$command: modified submodule does not update submodule work tree to invalid commit" '
+               prolog &&
+               reset_work_tree_to_interested add_sub1 &&
+               (
+                       cd submodule_update &&
+                       git branch -t invalid_sub1 origin/invalid_sub1 &&
+                       test_must_fail $command invalid_sub1 &&
+                       test_superproject_content origin/add_sub1 &&
+                       test_submodule_content sub1 origin/add_sub1
+               )
+       '
+       # Updating a submodule from an invalid sha1 updates
+       test_expect_success "$command: modified submodule does not 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
+               )
+       '
+}
diff --git a/t/perf/p0004-lazy-init-name-hash.sh b/t/perf/p0004-lazy-init-name-hash.sh
new file mode 100755 (executable)
index 0000000..5afa8c8
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='Tests multi-threaded lazy_init_name_hash'
+. ./perf-lib.sh
+
+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_expect_success 'multithreaded should be faster' '
+       $GIT_BUILD_DIR/t/helper/test-lazy-init-name-hash$X --perf >out.perf
+'
+
+test_done
diff --git a/t/perf/p0005-status.sh b/t/perf/p0005-status.sh
new file mode 100755 (executable)
index 0000000..0b0aa98
--- /dev/null
@@ -0,0 +1,49 @@
+#!/bin/sh
+#
+# This test measures the performance of various read-tree
+# and status operations.  It is primarily interested in
+# the algorithmic costs of index operations and recursive
+# tree traversal -- and NOT disk I/O on thousands of files.
+
+test_description="Tests performance of read-tree"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# If the test repo was generated by ./repos/many-files.sh
+# then we know something about the data shape and branches,
+# so we can isolate testing to the ballast-related commits
+# and setup sparse-checkout so we don't have to populate
+# the ballast files and directories.
+#
+# Otherwise, we make some general assumptions about the
+# repo and consider the entire history of the current
+# branch to be the ballast.
+
+test_expect_success "setup repo" '
+       if git rev-parse --verify refs/heads/p0006-ballast^{commit}
+       then
+               echo Assuming synthetic repo from many-files.sh
+               git branch br_base            master
+               git branch br_ballast         p0006-ballast
+               git config --local core.sparsecheckout 1
+               cat >.git/info/sparse-checkout <<-EOF
+               /*
+               !ballast/*
+               EOF
+       else
+               echo Assuming non-synthetic repo...
+               git branch br_base            $(git rev-list HEAD | tail -n 1)
+               git branch br_ballast         HEAD
+       fi &&
+       git checkout -q br_ballast &&
+       nr_files=$(git ls-files | wc -l)
+'
+
+test_perf "read-tree status br_ballast ($nr_files)" '
+       git read-tree HEAD &&
+       git status
+'
+
+test_done
index e424de5363848233d2c1d9807df0a01411fd0d6d..c4814d248fc96e7dc0177b6987baa18853c6213c 100755 (executable)
@@ -315,6 +315,20 @@ test_expect_success 'init with separate gitdir' '
        test_path_is_dir realgitdir/refs
 '
 
+test_expect_success 'init in long base path' '
+       # exceed initial buffer size of strbuf_getcwd()
+       component=123456789abcdef &&
+       test_when_finished "chmod 0700 $component; rm -rf $component" &&
+       p31=$component/$component &&
+       p127=$p31/$p31/$p31/$p31 &&
+       mkdir -p $p127 &&
+       chmod 0111 $component &&
+       (
+               cd $p127 &&
+               git init newdir
+       )
+'
+
 test_expect_success 're-init on .git file' '
        ( cd newdir && git init )
 '
diff --git a/t/t0013-sha1dc.sh b/t/t0013-sha1dc.sh
new file mode 100755 (executable)
index 0000000..6d655cb
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='test sha1 collision detection'
+. ./test-lib.sh
+TEST_DATA="$TEST_DIRECTORY/t0013"
+
+if test -z "$DC_SHA1"
+then
+       skip_all='skipping sha1 collision tests, DC_SHA1 not set'
+       test_done
+fi
+
+test_expect_success 'test-sha1 detects shattered pdf' '
+       test_must_fail test-sha1 <"$TEST_DATA/shattered-1.pdf" 2>err &&
+       test_i18ngrep collision err &&
+       grep 38762cf7f55934b34d179ae6a4c80cadccbb7f0a err
+'
+
+test_done
diff --git a/t/t0013/shattered-1.pdf b/t/t0013/shattered-1.pdf
new file mode 100644 (file)
index 0000000..ba9aaa1
Binary files /dev/null and b/t/t0013/shattered-1.pdf differ
index d0bee08b2e2add3caa82967e120157e7112385a4..89826c568b14659df98c1150fc3d021d375f4d9c 100755 (executable)
@@ -152,4 +152,30 @@ test_expect_success 'eol=crlf _does_ normalize binary files' '
        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 82c8411210831fbfbd3151b8e19dba9ee004fab0..fd92533acf488fd7d1aedd8152b9edaafc9bd87d 100755 (executable)
@@ -12,7 +12,100 @@ test -z "$NO_UNIX_SOCKETS" || {
 # don't leave a stale daemon running
 trap 'code=$?; git credential-cache exit; (exit $code); die' EXIT
 
+# test that the daemon works with no special setup
 helper_test cache
+
+test_expect_success 'socket defaults to ~/.cache/git/credential/socket' '
+       test_when_finished "
+               git credential-cache exit &&
+               rmdir -p .cache/git/credential/
+       " &&
+       test_path_is_missing "$HOME/.git-credential-cache" &&
+       test -S "$HOME/.cache/git/credential/socket"
+'
+
+XDG_CACHE_HOME="$HOME/xdg"
+export XDG_CACHE_HOME
+# test behavior when XDG_CACHE_HOME is set
+helper_test cache
+
+test_expect_success "use custom XDG_CACHE_HOME if set and default sockets are not created" '
+       test_when_finished "git credential-cache exit" &&
+       test -S "$XDG_CACHE_HOME/git/credential/socket" &&
+       test_path_is_missing "$HOME/.git-credential-cache/socket" &&
+       test_path_is_missing "$HOME/.cache/git/credential/socket"
+'
+unset XDG_CACHE_HOME
+
+test_expect_success 'credential-cache --socket option overrides default location' '
+       test_when_finished "
+               git credential-cache exit --socket \"\$HOME/dir/socket\" &&
+               rmdir \"\$HOME/dir\"
+       " &&
+       check approve "cache --socket \"\$HOME/dir/socket\"" <<-\EOF &&
+       protocol=https
+       host=example.com
+       username=store-user
+       password=store-pass
+       EOF
+       test -S "$HOME/dir/socket"
+'
+
+test_expect_success "use custom XDG_CACHE_HOME even if xdg socket exists" '
+       test_when_finished "
+               git credential-cache exit &&
+               sane_unset XDG_CACHE_HOME
+       " &&
+       check approve cache <<-\EOF &&
+       protocol=https
+       host=example.com
+       username=store-user
+       password=store-pass
+       EOF
+       test -S "$HOME/.cache/git/credential/socket" &&
+       XDG_CACHE_HOME="$HOME/xdg" &&
+       export XDG_CACHE_HOME &&
+       check approve cache <<-\EOF &&
+       protocol=https
+       host=example.com
+       username=store-user
+       password=store-pass
+       EOF
+       test -S "$XDG_CACHE_HOME/git/credential/socket"
+'
+
+test_expect_success 'use user socket if user directory exists' '
+       test_when_finished "
+               git credential-cache exit &&
+               rmdir \"\$HOME/.git-credential-cache/\"
+       " &&
+       mkdir -p -m 700 "$HOME/.git-credential-cache/" &&
+       check approve cache <<-\EOF &&
+       protocol=https
+       host=example.com
+       username=store-user
+       password=store-pass
+       EOF
+       test -S "$HOME/.git-credential-cache/socket"
+'
+
+test_expect_success SYMLINKS 'use user socket if user directory is a symlink to a directory' '
+       test_when_finished "
+               git credential-cache exit &&
+               rmdir \"\$HOME/dir/\" &&
+               rm \"\$HOME/.git-credential-cache\"
+       " &&
+       mkdir -p -m 700 "$HOME/dir/" &&
+       ln -s "$HOME/dir" "$HOME/.git-credential-cache" &&
+       check approve cache <<-\EOF &&
+       protocol=https
+       host=example.com
+       username=store-user
+       password=store-pass
+       EOF
+       test -S "$HOME/.git-credential-cache/socket"
+'
+
 helper_test_timeout cache --timeout=1
 
 # we can't rely on our "trap" above working after test_done,
index c5245c5cb4c1c086b6fe7205f34b9b5ca53e2e59..532682f51c4f9cefc34a44cf7e7f4231c60b7f65 100755 (executable)
@@ -134,6 +134,16 @@ test_expect_success 'gitattributes also work in a subdirectory' '
        )
 '
 
+test_expect_success '--path works in a subdirectory' '
+       (
+               cd subdir &&
+               path1_sha=$(git hash-object --path=../file1 ../file0) &&
+               path0_sha=$(git hash-object --path=../file0 ../file1) &&
+               test "$file0_sha" = "$path0_sha" &&
+               test "$file1_sha" = "$path1_sha"
+       )
+'
+
 test_expect_success 'check that --no-filters option works' '
        nofilters_file1=$(git hash-object --no-filters file1) &&
        test "$file0_sha" = "$nofilters_file1" &&
index 20526aed34212597179eff4bec41866ae3254183..de1ba02dc5b1c76ea52eb490298bcfcefc342b51 100755 (executable)
@@ -5,6 +5,14 @@ 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_forced_switch_recursing "git read-tree --recurse-submodules -u --reset"
+
 test_submodule_switch "git read-tree -u -m"
 
 test_submodule_forced_switch "git read-tree -u --reset"
index 3f8705139d74e8e358ae60cabe5d1cd92d316489..ac1f189fd29b171c16af17e1d3ddda48f557df89 100755 (executable)
@@ -21,6 +21,14 @@ test_expect_success 'setup corrupt repo' '
                cd bit-error &&
                test_commit content &&
                corrupt_byte HEAD:content.t 10
+       ) &&
+       git init no-bit-error &&
+       (
+               # distinct commit from bit-error, but containing a
+               # non-corrupted version of the same blob
+               cd no-bit-error &&
+               test_tick &&
+               test_commit content
        )
 '
 
@@ -53,6 +61,13 @@ test_expect_success 'streaming a corrupt blob fails' '
        )
 '
 
+test_expect_success 'getting type of a corrupt blob fails' '
+       (
+               cd bit-error &&
+               test_must_fail git cat-file -s HEAD:content.t
+       )
+'
+
 test_expect_success 'read-tree -u detects bit-errors in blobs' '
        (
                cd bit-error &&
@@ -101,4 +116,13 @@ test_expect_failure 'clone --local detects misnamed objects' '
        test_must_fail git clone --local misnamed misnamed-checkout
 '
 
+test_expect_success 'fetch into corrupted repo with index-pack' '
+       (
+               cd bit-error &&
+               test_must_fail git -c transfer.unpackLimit=1 \
+                       fetch ../no-bit-error 2>stderr &&
+               test_i18ngrep ! -i collision stderr
+       )
+'
+
 test_done
index 9ba2ba11c3cac88154dbb2d522b43adf98a8dee7..8fbc7a029f79b1fd9ffae2fa6183ebdd4bbbb923 100755 (executable)
@@ -3,6 +3,16 @@
 test_description='test config file include directives'
 . ./test-lib.sh
 
+# Force setup_explicit_git_dir() to run until the end. This is needed
+# by some tests to make sure real_path() is called on $GIT_DIR. The
+# caller needs to make sure git commands are run from a subdirectory
+# though or real_path() will not be called.
+force_setup_explicit_git_dir() {
+    GIT_DIR="$(pwd)/.git"
+    GIT_WORK_TREE="$(pwd)"
+    export GIT_DIR GIT_WORK_TREE
+}
+
 test_expect_success 'include file by absolute path' '
        echo "[test]one = 1" >one &&
        echo "[include]path = \"$(pwd)/one\"" >.gitconfig &&
@@ -102,7 +112,7 @@ test_expect_success 'config modification does not affect includes' '
 
 test_expect_success 'missing include files are ignored' '
        cat >.gitconfig <<-\EOF &&
-       [include]path = foo
+       [include]path = non-existent
        [test]value = yes
        EOF
        echo yes >expect &&
@@ -152,6 +162,106 @@ test_expect_success 'relative includes from stdin line fail' '
        test_must_fail git config --file - test.one
 '
 
+test_expect_success 'conditional include, both unanchored' '
+       git init foo &&
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:foo/\"]path=bar" >>.git/config &&
+               echo "[test]one=1" >.git/bar &&
+               echo 1 >expect &&
+               git config test.one >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, $HOME expansion' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:~/foo/\"]path=bar2" >>.git/config &&
+               echo "[test]two=2" >.git/bar2 &&
+               echo 2 >expect &&
+               git config test.two >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, full pattern' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:**/foo/**\"]path=bar3" >>.git/config &&
+               echo "[test]three=3" >.git/bar3 &&
+               echo 3 >expect &&
+               git config test.three >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, relative path' '
+       echo "[includeIf \"gitdir:./foo/.git\"]path=bar4" >>.gitconfig &&
+       echo "[test]four=4" >bar4 &&
+       (
+               cd foo &&
+               echo 4 >expect &&
+               git config test.four >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'conditional include, both unanchored, icase' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir/i:FOO/\"]path=bar5" >>.git/config &&
+               echo "[test]five=5" >.git/bar5 &&
+               echo 5 >expect &&
+               git config test.five >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success SYMLINKS 'conditional include, set up symlinked $HOME' '
+       mkdir real-home &&
+       ln -s real-home home &&
+       (
+               HOME="$TRASH_DIRECTORY/home" &&
+               export HOME &&
+               cd "$HOME" &&
+
+               git init foo &&
+               cd foo &&
+               mkdir sub
+       )
+'
+
+test_expect_success SYMLINKS 'conditional include, $HOME expansion with symlinks' '
+       (
+               HOME="$TRASH_DIRECTORY/home" &&
+               export HOME &&
+               cd "$HOME"/foo &&
+
+               echo "[includeIf \"gitdir:~/foo/\"]path=bar2" >>.git/config &&
+               echo "[test]two=2" >.git/bar2 &&
+               echo 2 >expect &&
+               force_setup_explicit_git_dir &&
+               git -C sub config test.two >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success SYMLINKS 'conditional include, relative path with symlinks' '
+       echo "[includeIf \"gitdir:./foo/.git\"]path=bar4" >home/.gitconfig &&
+       echo "[test]four=4" >home/bar4 &&
+       (
+               HOME="$TRASH_DIRECTORY/home" &&
+               export HOME &&
+               cd "$HOME"/foo &&
+
+               echo 4 >expect &&
+               force_setup_explicit_git_dir &&
+               git -C sub config test.four >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success 'include cycles are detected' '
        cat >.gitconfig <<-\EOF &&
        [test]value = gitconfig
diff --git a/t/t1309-early-config.sh b/t/t1309-early-config.sh
new file mode 100755 (executable)
index 0000000..b97357b
--- /dev/null
@@ -0,0 +1,74 @@
+#!/bin/sh
+
+test_description='Test read_early_config()'
+
+. ./test-lib.sh
+
+test_expect_success 'read early config' '
+       test_config early.config correct &&
+       test-config read_early_config early.config >output &&
+       test correct = "$(cat output)"
+'
+
+test_expect_success 'in a sub-directory' '
+       test_config early.config sub &&
+       mkdir -p sub &&
+       (
+               cd sub &&
+               test-config read_early_config early.config
+       ) >output &&
+       test sub = "$(cat output)"
+'
+
+test_expect_success 'ceiling' '
+       test_config early.config ceiling &&
+       mkdir -p sub &&
+       (
+               GIT_CEILING_DIRECTORIES="$PWD" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd sub &&
+               test-config read_early_config early.config
+       ) >output &&
+       test -z "$(cat output)"
+'
+
+test_expect_success 'ceiling #2' '
+       mkdir -p xdg/git &&
+       git config -f xdg/git/config early.config xdg &&
+       test_config early.config ceiling &&
+       mkdir -p sub &&
+       (
+               XDG_CONFIG_HOME="$PWD"/xdg &&
+               GIT_CEILING_DIRECTORIES="$PWD" &&
+               export GIT_CEILING_DIRECTORIES XDG_CONFIG_HOME &&
+               cd sub &&
+               test-config read_early_config early.config
+       ) >output &&
+       test xdg = "$(cat output)"
+'
+
+test_with_config () {
+       rm -rf throwaway &&
+       git init throwaway &&
+       (
+               cd throwaway &&
+               echo "$*" >.git/config &&
+               test-config read_early_config early.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_expect_failure 'ignore .git/ with invalid repository version' '
+       test_with_config "[core]repositoryformatversion = invalid"
+'
+
+
+test_expect_failure 'ignore .git/ with invalid config' '
+       test_with_config "["
+'
+
+test_done
index 825422341de5d80247d4a2ac689d6fc799f9a9a0..dc98b4bc6dc7aab41eca38d9e1086ad2b6701fd3 100755 (executable)
@@ -35,73 +35,73 @@ test_expect_success setup '
        cd -
 '
 
-test_expect_success \
-       "create $m" \
-       "git update-ref $m $A &&
-        test $A"' = $(cat .git/'"$m"')'
-test_expect_success \
-       "create $m" \
-       "git update-ref $m $B $A &&
-        test $B"' = $(cat .git/'"$m"')'
+test_expect_success "create $m" '
+       git update-ref $m $A &&
+       test $A = $(cat .git/$m)
+'
+test_expect_success "create $m with oldvalue verification" '
+       git update-ref $m $B $A &&
+       test $B = $(cat .git/$m)
+'
 test_expect_success "fail to delete $m with stale ref" '
        test_must_fail git update-ref -d $m $A &&
        test $B = "$(cat .git/$m)"
 '
 test_expect_success "delete $m" '
+       test_when_finished "rm -f .git/$m" &&
        git update-ref -d $m $B &&
-       ! test -f .git/$m
+       test_path_is_missing .git/$m
 '
-rm -f .git/$m
 
-test_expect_success "delete $m without oldvalue verification" "
+test_expect_success "delete $m without oldvalue verification" '
+       test_when_finished "rm -f .git/$m" &&
        git update-ref $m $A &&
-       test $A = \$(cat .git/$m) &&
+       test $A = $(cat .git/$m) &&
        git update-ref -d $m &&
-       ! test -f .git/$m
-"
-rm -f .git/$m
-
-test_expect_success \
-       "fail to create $n" \
-       "touch .git/$n_dir &&
-        test_must_fail git update-ref $n $A >out 2>err"
-rm -f .git/$n_dir out err
-
-test_expect_success \
-       "create $m (by HEAD)" \
-       "git update-ref HEAD $A &&
-        test $A"' = $(cat .git/'"$m"')'
-test_expect_success \
-       "create $m (by HEAD)" \
-       "git update-ref HEAD $B $A &&
-        test $B"' = $(cat .git/'"$m"')'
+       test_path_is_missing .git/$m
+'
+
+test_expect_success "fail to create $n" '
+       test_when_finished "rm -f .git/$n_dir" &&
+       touch .git/$n_dir &&
+       test_must_fail git update-ref $n $A
+'
+
+test_expect_success "create $m (by HEAD)" '
+       git update-ref HEAD $A &&
+       test $A = $(cat .git/$m)
+'
+test_expect_success "create $m (by HEAD) with oldvalue verification" '
+       git update-ref HEAD $B $A &&
+       test $B = $(cat .git/$m)
+'
 test_expect_success "fail to delete $m (by HEAD) with stale ref" '
        test_must_fail git update-ref -d HEAD $A &&
        test $B = $(cat .git/$m)
 '
 test_expect_success "delete $m (by HEAD)" '
+       test_when_finished "rm -f .git/$m" &&
        git update-ref -d HEAD $B &&
-       ! test -f .git/$m
+       test_path_is_missing .git/$m
 '
-rm -f .git/$m
 
 test_expect_success "deleting current branch adds message to HEAD's log" '
+       test_when_finished "rm -f .git/$m" &&
        git update-ref $m $A &&
        git symbolic-ref HEAD $m &&
        git update-ref -m delete-$m -d $m &&
-       ! test -f .git/$m &&
+       test_path_is_missing .git/$m &&
        grep "delete-$m$" .git/logs/HEAD
 '
-rm -f .git/$m
 
 test_expect_success "deleting by HEAD adds message to HEAD's log" '
+       test_when_finished "rm -f .git/$m" &&
        git update-ref $m $A &&
        git symbolic-ref HEAD $m &&
        git update-ref -m delete-by-head -d HEAD &&
-       ! test -f .git/$m &&
+       test_path_is_missing .git/$m &&
        grep "delete-by-head$" .git/logs/HEAD
 '
-rm -f .git/$m
 
 test_expect_success 'update-ref does not create reflogs by default' '
        test_when_finished "git update-ref -d $outside" &&
@@ -176,40 +176,41 @@ test_expect_success '--no-create-reflog overrides core.logAllRefUpdates=always'
        test_must_fail git reflog exists $outside
 '
 
-test_expect_success \
-       "create $m (by HEAD)" \
-       "git update-ref HEAD $A &&
-        test $A"' = $(cat .git/'"$m"')'
-test_expect_success \
-       "pack refs" \
-       "git pack-refs --all"
-test_expect_success \
-       "move $m (by HEAD)" \
-       "git update-ref HEAD $B $A &&
-        test $B"' = $(cat .git/'"$m"')'
+test_expect_success "create $m (by HEAD)" '
+       git update-ref HEAD $A &&
+       test $A = $(cat .git/$m)
+'
+test_expect_success 'pack refs' '
+       git pack-refs --all
+'
+test_expect_success "move $m (by HEAD)" '
+       git update-ref HEAD $B $A &&
+       test $B = $(cat .git/$m)
+'
 test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" '
+       test_when_finished "rm -f .git/$m" &&
        git update-ref -d HEAD $B &&
        ! grep "$m" .git/packed-refs &&
-       ! test -f .git/$m
+       test_path_is_missing .git/$m
 '
-rm -f .git/$m
 
 cp -f .git/HEAD .git/HEAD.orig
-test_expect_success "delete symref without dereference" '
+test_expect_success 'delete symref without dereference' '
+       test_when_finished "cp -f .git/HEAD.orig .git/HEAD" &&
        git update-ref --no-deref -d HEAD &&
-       ! test -f .git/HEAD
+       test_path_is_missing .git/HEAD
 '
-cp -f .git/HEAD.orig .git/HEAD
 
-test_expect_success "delete symref without dereference when the referred ref is packed" '
+test_expect_success 'delete symref without dereference when the referred ref is packed' '
+       test_when_finished "cp -f .git/HEAD.orig .git/HEAD" &&
        echo foo >foo.c &&
        git add foo.c &&
        git commit -m foo &&
        git pack-refs --all &&
        git update-ref --no-deref -d HEAD &&
-       ! test -f .git/HEAD
+       test_path_is_missing .git/HEAD
 '
-cp -f .git/HEAD.orig .git/HEAD
+
 git update-ref -d $m
 
 test_expect_success 'update-ref -d is not confused by self-reference' '
@@ -238,67 +239,70 @@ test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
        test_path_is_missing .git/refs/heads/ref-to-bad
 '
 
-test_expect_success '(not) create HEAD with old sha1' "
+test_expect_success '(not) create HEAD with old sha1' '
        test_must_fail git update-ref HEAD $A $B
-"
-test_expect_success "(not) prior created .git/$m" "
-       ! test -f .git/$m
-"
-rm -f .git/$m
-
-test_expect_success \
-       "create HEAD" \
-       "git update-ref HEAD $A"
-test_expect_success '(not) change HEAD with wrong SHA1' "
+'
+test_expect_success "(not) prior created .git/$m" '
+       test_when_finished "rm -f .git/$m" &&
+       test_path_is_missing .git/$m
+'
+
+test_expect_success 'create HEAD' '
+       git update-ref HEAD $A
+'
+test_expect_success '(not) change HEAD with wrong SHA1' '
        test_must_fail git update-ref HEAD $B $Z
-"
-test_expect_success "(not) changed .git/$m" "
-       ! test $B"' = $(cat .git/'"$m"')
 '
-rm -f .git/$m
+test_expect_success "(not) changed .git/$m" '
+       test_when_finished "rm -f .git/$m" &&
+       ! test $B = $(cat .git/$m)
+'
 
 rm -f .git/logs/refs/heads/master
-test_expect_success \
-       "create $m (logged by touch)" \
-       'GIT_COMMITTER_DATE="2005-05-26 23:30" \
-        git update-ref --create-reflog HEAD '"$A"' -m "Initial Creation" &&
-        test '"$A"' = $(cat .git/'"$m"')'
-test_expect_success \
-       "update $m (logged by touch)" \
-       'GIT_COMMITTER_DATE="2005-05-26 23:31" \
-        git update-ref HEAD'" $B $A "'-m "Switch" &&
-        test '"$B"' = $(cat .git/'"$m"')'
-test_expect_success \
-       "set $m (logged by touch)" \
-       'GIT_COMMITTER_DATE="2005-05-26 23:41" \
-        git update-ref HEAD'" $A &&
-        test $A"' = $(cat .git/'"$m"')'
-
-test_expect_success "empty directory removal" '
+test_expect_success "create $m (logged by touch)" '
+       test_config core.logAllRefUpdates false &&
+       GIT_COMMITTER_DATE="2005-05-26 23:30" \
+       git update-ref --create-reflog HEAD $A -m "Initial Creation" &&
+       test $A = $(cat .git/$m)
+'
+test_expect_success "update $m (logged by touch)" '
+       test_config core.logAllRefUpdates false &&
+       GIT_COMMITTER_DATE="2005-05-26 23:31" \
+       git update-ref HEAD $B $A -m "Switch" &&
+       test $B = $(cat .git/$m)
+'
+test_expect_success "set $m (logged by touch)" '
+       test_config core.logAllRefUpdates false &&
+       GIT_COMMITTER_DATE="2005-05-26 23:41" \
+       git update-ref HEAD $A &&
+       test $A = $(cat .git/$m)
+'
+
+test_expect_success 'empty directory removal' '
        git branch d1/d2/r1 HEAD &&
        git branch d1/r2 HEAD &&
-       test -f .git/refs/heads/d1/d2/r1 &&
-       test -f .git/logs/refs/heads/d1/d2/r1 &&
+       test_path_is_file .git/refs/heads/d1/d2/r1 &&
+       test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
        git branch -d d1/d2/r1 &&
-       ! test -e .git/refs/heads/d1/d2 &&
-       ! test -e .git/logs/refs/heads/d1/d2 &&
-       test -f .git/refs/heads/d1/r2 &&
-       test -f .git/logs/refs/heads/d1/r2
+       test_path_is_missing .git/refs/heads/d1/d2 &&
+       test_path_is_missing .git/logs/refs/heads/d1/d2 &&
+       test_path_is_file .git/refs/heads/d1/r2 &&
+       test_path_is_file .git/logs/refs/heads/d1/r2
 '
 
-test_expect_success "symref empty directory removal" '
+test_expect_success 'symref empty directory removal' '
        git branch e1/e2/r1 HEAD &&
        git branch e1/r2 HEAD &&
        git checkout e1/e2/r1 &&
        test_when_finished "git checkout master" &&
-       test -f .git/refs/heads/e1/e2/r1 &&
-       test -f .git/logs/refs/heads/e1/e2/r1 &&
+       test_path_is_file .git/refs/heads/e1/e2/r1 &&
+       test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
        git update-ref -d HEAD &&
-       ! test -e .git/refs/heads/e1/e2 &&
-       ! test -e .git/logs/refs/heads/e1/e2 &&
-       test -f .git/refs/heads/e1/r2 &&
-       test -f .git/logs/refs/heads/e1/r2 &&
-       test -f .git/logs/HEAD
+       test_path_is_missing .git/refs/heads/e1/e2 &&
+       test_path_is_missing .git/logs/refs/heads/e1/e2 &&
+       test_path_is_file .git/refs/heads/e1/r2 &&
+       test_path_is_file .git/logs/refs/heads/e1/r2 &&
+       test_path_is_file .git/logs/HEAD
 '
 
 cat >expect <<EOF
@@ -306,41 +310,39 @@ $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 Initial Creati
 $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150260 +0000      Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000
 EOF
-test_expect_success \
-       "verifying $m's log" \
-       "test_cmp expect .git/logs/$m"
-rm -rf .git/$m .git/logs expect
-
-test_expect_success \
-       'enable core.logAllRefUpdates' \
-       'git config core.logAllRefUpdates true &&
-        test true = $(git config --bool --get core.logAllRefUpdates)'
-
-test_expect_success \
-       "create $m (logged by config)" \
-       'GIT_COMMITTER_DATE="2005-05-26 23:32" \
-        git update-ref HEAD'" $A "'-m "Initial Creation" &&
-        test '"$A"' = $(cat .git/'"$m"')'
-test_expect_success \
-       "update $m (logged by config)" \
-       'GIT_COMMITTER_DATE="2005-05-26 23:33" \
-        git update-ref HEAD'" $B $A "'-m "Switch" &&
-        test '"$B"' = $(cat .git/'"$m"')'
-test_expect_success \
-       "set $m (logged by config)" \
-       'GIT_COMMITTER_DATE="2005-05-26 23:43" \
-        git update-ref HEAD '"$A &&
-        test $A"' = $(cat .git/'"$m"')'
+test_expect_success "verifying $m's log (logged by touch)" '
+       test_when_finished "rm -rf .git/$m .git/logs expect" &&
+       test_cmp expect .git/logs/$m
+'
+
+test_expect_success "create $m (logged by config)" '
+       test_config core.logAllRefUpdates true &&
+       GIT_COMMITTER_DATE="2005-05-26 23:32" \
+       git update-ref HEAD $A -m "Initial Creation" &&
+       test $A = $(cat .git/$m)
+'
+test_expect_success "update $m (logged by config)" '
+       test_config core.logAllRefUpdates true &&
+       GIT_COMMITTER_DATE="2005-05-26 23:33" \
+       git update-ref HEAD'" $B $A "'-m "Switch" &&
+       test $B = $(cat .git/$m)
+'
+test_expect_success "set $m (logged by config)" '
+       test_config core.logAllRefUpdates true &&
+       GIT_COMMITTER_DATE="2005-05-26 23:43" \
+       git update-ref HEAD $A &&
+       test $A = $(cat .git/$m)
+'
 
 cat >expect <<EOF
 $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150320 +0000      Initial Creation
 $A $B $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150380 +0000      Switch
 $B $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150980 +0000
 EOF
-test_expect_success \
-       "verifying $m's log" \
-       'test_cmp expect .git/logs/$m'
-rm -f .git/$m .git/logs/$m expect
+test_expect_success "verifying $m's log (logged by config)" '
+       test_when_finished "rm -f .git/$m .git/logs/$m expect" &&
+       test_cmp expect .git/logs/$m
+'
 
 git update-ref $m $D
 cat >.git/logs/$m <<EOF
@@ -354,86 +356,85 @@ EOF
 ed="Thu, 26 May 2005 18:32:00 -0500"
 gd="Thu, 26 May 2005 18:33:00 -0500"
 ld="Thu, 26 May 2005 18:43:00 -0500"
-test_expect_success \
-       'Query "master@{May 25 2005}" (before history)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
-        test '"$C"' = $(cat o) &&
-        test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
-test_expect_success \
-       "Query master@{2005-05-25} (before history)" \
-       'rm -f o e &&
-        git rev-parse --verify master@{2005-05-25} >o 2>e &&
-        test '"$C"' = $(cat o) &&
-        echo test "warning: Log for '\'master\'' only goes back to $ed." = "$(cat e)"'
-test_expect_success \
-       'Query "master@{May 26 2005 23:31:59}" (1 second before history)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
-        test '"$C"' = $(cat o) &&
-        test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"'
-test_expect_success \
-       'Query "master@{May 26 2005 23:32:00}" (exactly history start)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
-        test '"$C"' = $(cat o) &&
-        test "" = "$(cat e)"'
-test_expect_success \
-       'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
-        test '"$A"' = $(cat o) &&
-        test "" = "$(cat e)"'
-test_expect_success \
-       'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
-        test '"$B"' = $(cat o) &&
-        test "warning: Log for ref '"$m has gap after $gd"'." = "$(cat e)"'
-test_expect_success \
-       'Query "master@{2005-05-26 23:38:00}" (middle of history)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
-        test '"$Z"' = $(cat o) &&
-        test "" = "$(cat e)"'
-test_expect_success \
-       'Query "master@{2005-05-26 23:43:00}" (exact end of history)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
-        test '"$E"' = $(cat o) &&
-        test "" = "$(cat e)"'
-test_expect_success \
-       'Query "master@{2005-05-28}" (past end of history)' \
-       'rm -f o e &&
-        git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
-        test '"$D"' = $(cat o) &&
-        test "warning: Log for ref '"$m unexpectedly ended on $ld"'." = "$(cat e)"'
-
+test_expect_success 'Query "master@{May 25 2005}" (before history)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{May 25 2005}" >o 2>e &&
+       test $C = $(cat o) &&
+       test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+'
+test_expect_success 'Query master@{2005-05-25} (before history)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify master@{2005-05-25} >o 2>e &&
+       test $C = $(cat o) &&
+       echo test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+'
+test_expect_success 'Query "master@{May 26 2005 23:31:59}" (1 second before history)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{May 26 2005 23:31:59}" >o 2>e &&
+       test $C = $(cat o) &&
+       test "warning: Log for '\''master'\'' only goes back to $ed." = "$(cat e)"
+'
+test_expect_success 'Query "master@{May 26 2005 23:32:00}" (exactly history start)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{May 26 2005 23:32:00}" >o 2>e &&
+       test $C = $(cat o) &&
+       test "" = "$(cat e)"
+'
+test_expect_success 'Query "master@{May 26 2005 23:32:30}" (first non-creation change)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{May 26 2005 23:32:30}" >o 2>e &&
+       test $A = $(cat o) &&
+       test "" = "$(cat e)"
+'
+test_expect_success 'Query "master@{2005-05-26 23:33:01}" (middle of history with gap)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{2005-05-26 23:33:01}" >o 2>e &&
+       test $B = $(cat o) &&
+       test "warning: Log for ref $m has gap after $gd." = "$(cat e)"
+'
+test_expect_success 'Query "master@{2005-05-26 23:38:00}" (middle of history)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{2005-05-26 23:38:00}" >o 2>e &&
+       test $Z = $(cat o) &&
+       test "" = "$(cat e)"
+'
+test_expect_success 'Query "master@{2005-05-26 23:43:00}" (exact end of history)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{2005-05-26 23:43:00}" >o 2>e &&
+       test $E = $(cat o) &&
+       test "" = "$(cat e)"
+'
+test_expect_success 'Query "master@{2005-05-28}" (past end of history)' '
+       test_when_finished "rm -f o e" &&
+       git rev-parse --verify "master@{2005-05-28}" >o 2>e &&
+       test $D = $(cat o) &&
+       test "warning: Log for ref $m unexpectedly ended on $ld." = "$(cat e)"
+'
 
 rm -f .git/$m .git/logs/$m expect
 
-test_expect_success \
-    'creating initial files' \
-    'echo TEST >F &&
-     git add F &&
-        GIT_AUTHOR_DATE="2005-05-26 23:30" \
-        GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a &&
-        h_TEST=$(git rev-parse --verify HEAD) &&
-        echo The other day this did not work. >M &&
-        echo And then Bob told me how to fix it. >>M &&
-        echo OTHER >F &&
-        GIT_AUTHOR_DATE="2005-05-26 23:41" \
-        GIT_COMMITTER_DATE="2005-05-26 23:41" git commit -F M -a &&
-        h_OTHER=$(git rev-parse --verify HEAD) &&
-        GIT_AUTHOR_DATE="2005-05-26 23:44" \
-        GIT_COMMITTER_DATE="2005-05-26 23:44" git commit --amend &&
-        h_FIXED=$(git rev-parse --verify HEAD) &&
-        echo Merged initial commit and a later commit. >M &&
-        echo $h_TEST >.git/MERGE_HEAD &&
-        GIT_AUTHOR_DATE="2005-05-26 23:45" \
-        GIT_COMMITTER_DATE="2005-05-26 23:45" git commit -F M &&
-        h_MERGED=$(git rev-parse --verify HEAD) &&
-        rm -f M'
+test_expect_success 'creating initial files' '
+       test_when_finished rm -f M &&
+       echo TEST >F &&
+       git add F &&
+       GIT_AUTHOR_DATE="2005-05-26 23:30" \
+       GIT_COMMITTER_DATE="2005-05-26 23:30" git commit -m add -a &&
+       h_TEST=$(git rev-parse --verify HEAD) &&
+       echo The other day this did not work. >M &&
+       echo And then Bob told me how to fix it. >>M &&
+       echo OTHER >F &&
+       GIT_AUTHOR_DATE="2005-05-26 23:41" \
+       GIT_COMMITTER_DATE="2005-05-26 23:41" git commit -F M -a &&
+       h_OTHER=$(git rev-parse --verify HEAD) &&
+       GIT_AUTHOR_DATE="2005-05-26 23:44" \
+       GIT_COMMITTER_DATE="2005-05-26 23:44" git commit --amend &&
+       h_FIXED=$(git rev-parse --verify HEAD) &&
+       echo Merged initial commit and a later commit. >M &&
+       echo $h_TEST >.git/MERGE_HEAD &&
+       GIT_AUTHOR_DATE="2005-05-26 23:45" \
+       GIT_COMMITTER_DATE="2005-05-26 23:45" git commit -F M &&
+       h_MERGED=$(git rev-parse --verify HEAD)
+'
 
 cat >expect <<EOF
 $Z $h_TEST $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 commit (initial): add
@@ -441,20 +442,20 @@ $h_TEST $h_OTHER $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150860 +0000      com
 $h_OTHER $h_FIXED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151040 +0000  commit (amend): The other day this did not work.
 $h_FIXED $h_MERGED $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117151100 +0000 commit (merge): Merged initial commit and a later commit.
 EOF
-test_expect_success \
-       'git commit logged updates' \
-       "test_cmp expect .git/logs/$m"
+test_expect_success 'git commit logged updates' '
+       test_cmp expect .git/logs/$m
+'
 unset h_TEST h_OTHER h_FIXED h_MERGED
 
-test_expect_success \
-       'git cat-file blob master:F (expect OTHER)' \
-       'test OTHER = $(git cat-file blob master:F)'
-test_expect_success \
-       'git cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' \
-       'test TEST = $(git cat-file blob "master@{2005-05-26 23:30}:F")'
-test_expect_success \
-       'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' \
-       'test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F")'
+test_expect_success 'git cat-file blob master:F (expect OTHER)' '
+       test OTHER = $(git cat-file blob master:F)
+'
+test_expect_success 'git cat-file blob master@{2005-05-26 23:30}:F (expect TEST)' '
+       test TEST = $(git cat-file blob "master@{2005-05-26 23:30}:F")
+'
+test_expect_success 'git cat-file blob master@{2005-05-26 23:42}:F (expect OTHER)' '
+       test OTHER = $(git cat-file blob "master@{2005-05-26 23:42}:F")
+'
 
 a=refs/heads/a
 b=refs/heads/b
diff --git a/t/t1405-main-ref-store.sh b/t/t1405-main-ref-store.sh
new file mode 100755 (executable)
index 0000000..490521f
--- /dev/null
@@ -0,0 +1,129 @@
+#!/bin/sh
+
+test_description='test main ref store api'
+
+. ./test-lib.sh
+
+RUN="test-ref-store main"
+
+test_expect_success 'pack_refs(PACK_REFS_ALL | PACK_REFS_PRUNE)' '
+       test_commit one &&
+       N=`find .git/refs -type f | wc -l` &&
+       test "$N" != 0 &&
+       $RUN pack-refs 3 &&
+       N=`find .git/refs -type f | wc -l`
+'
+
+test_expect_success 'peel_ref(new-tag)' '
+       git rev-parse HEAD >expected &&
+       git tag -a -m new-tag new-tag HEAD &&
+       $RUN peel-ref refs/tags/new-tag >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'create_symref(FOO, refs/heads/master)' '
+       $RUN create-symref FOO refs/heads/master nothing &&
+       echo refs/heads/master >expected &&
+       git symbolic-ref FOO >actual &&
+       test_cmp expected actual
+'
+
+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 &&
+       test_must_fail git rev-parse FOO -- &&
+       test_must_fail git rev-parse refs/tags/new-tag --
+'
+
+test_expect_success 'rename_refs(master, new-master)' '
+       git rev-parse master >expected &&
+       $RUN rename-ref refs/heads/master refs/heads/new-master &&
+       git rev-parse new-master >actual &&
+       test_cmp expected actual &&
+       test_commit recreate-master
+'
+
+test_expect_success 'for_each_ref(refs/heads/)' '
+       $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+       cat >expected <<-\EOF &&
+       master 0x0
+       new-master 0x0
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'for_each_ref() is sorted' '
+       $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+       sort actual > expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'resolve_ref(new-master)' '
+       SHA1=`git rev-parse new-master` &&
+       echo "$SHA1 refs/heads/new-master 0x0" >expected &&
+       $RUN resolve-ref refs/heads/new-master 0 >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'verify_ref(new-master)' '
+       $RUN verify-ref refs/heads/new-master
+'
+
+test_expect_success 'for_each_reflog()' '
+       $RUN for-each-reflog | sort | cut -c 42- >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       refs/heads/master 0x0
+       refs/heads/new-master 0x0
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'for_each_reflog_ent()' '
+       $RUN for-each-reflog-ent HEAD >actual &&
+       head -n1 actual | grep one &&
+       tail -n2 actual | head -n1 | grep recreate-master
+'
+
+test_expect_success 'for_each_reflog_ent_reverse()' '
+       $RUN for-each-reflog-ent-reverse HEAD >actual &&
+       head -n1 actual | grep recreate-master &&
+       tail -n2 actual | head -n1 | grep one
+'
+
+test_expect_success 'reflog_exists(HEAD)' '
+       $RUN reflog-exists HEAD
+'
+
+test_expect_success 'delete_reflog(HEAD)' '
+       $RUN delete-reflog HEAD &&
+       ! test -f .git/logs/HEAD
+'
+
+test_expect_success 'create-reflog(HEAD)' '
+       $RUN create-reflog HEAD 1 &&
+       test -f .git/logs/HEAD
+'
+
+test_expect_success 'delete_ref(refs/heads/foo)' '
+       git checkout -b foo &&
+       FOO_SHA1=`git rev-parse foo` &&
+       git checkout --detach &&
+       test_commit bar-commit &&
+       git checkout -b bar &&
+       BAR_SHA1=`git rev-parse bar` &&
+       $RUN update-ref updating refs/heads/foo $BAR_SHA1 $FOO_SHA1 0 &&
+       echo $BAR_SHA1 >expected &&
+       git rev-parse refs/heads/foo >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'delete_ref(refs/heads/foo)' '
+       SHA1=`git rev-parse foo` &&
+       git checkout --detach &&
+       $RUN delete-ref msg refs/heads/foo $SHA1 0 &&
+       test_must_fail git rev-parse refs/heads/foo --
+'
+
+test_done
diff --git a/t/t1406-submodule-ref-store.sh b/t/t1406-submodule-ref-store.sh
new file mode 100755 (executable)
index 0000000..13b5454
--- /dev/null
@@ -0,0 +1,101 @@
+#!/bin/sh
+
+test_description='test submodule ref store api'
+
+. ./test-lib.sh
+
+RUN="test-ref-store submodule:sub"
+
+test_expect_success 'setup' '
+       git init sub &&
+       (
+               cd sub &&
+               test_commit first &&
+               git checkout -b new-master
+       )
+'
+
+test_expect_success 'pack_refs() not allowed' '
+       test_must_fail $RUN pack-refs 3
+'
+
+test_expect_success 'peel_ref(new-tag)' '
+       git -C sub rev-parse HEAD >expected &&
+       git -C sub tag -a -m new-tag new-tag HEAD &&
+       $RUN peel-ref refs/tags/new-tag >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'create_symref() not allowed' '
+       test_must_fail $RUN create-symref FOO refs/heads/master nothing
+'
+
+test_expect_success 'delete_refs() not allowed' '
+       test_must_fail $RUN delete-refs 0 FOO refs/tags/new-tag
+'
+
+test_expect_success 'rename_refs() not allowed' '
+       test_must_fail $RUN rename-ref refs/heads/master refs/heads/new-master
+'
+
+test_expect_success 'for_each_ref(refs/heads/)' '
+       $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+       cat >expected <<-\EOF &&
+       master 0x0
+       new-master 0x0
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'for_each_ref() is sorted' '
+       $RUN for-each-ref refs/heads/ | cut -c 42- >actual &&
+       sort actual > expected &&
+       test_cmp expected actual
+'
+
+test_expect_success 'resolve_ref(master)' '
+       SHA1=`git -C sub rev-parse master` &&
+       echo "$SHA1 refs/heads/master 0x0" >expected &&
+       $RUN resolve-ref refs/heads/master 0 >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'verify_ref(new-master)' '
+       $RUN verify-ref refs/heads/new-master
+'
+
+test_expect_success 'for_each_reflog()' '
+       $RUN for-each-reflog | sort | cut -c 42- >actual &&
+       cat >expected <<-\EOF &&
+       HEAD 0x1
+       refs/heads/master 0x0
+       refs/heads/new-master 0x0
+       EOF
+       test_cmp expected actual
+'
+
+test_expect_success 'for_each_reflog_ent()' '
+       $RUN for-each-reflog-ent HEAD >actual && cat actual &&
+       head -n1 actual | grep first &&
+       tail -n2 actual | head -n1 | grep master.to.new
+'
+
+test_expect_success 'for_each_reflog_ent_reverse()' '
+       $RUN for-each-reflog-ent-reverse HEAD >actual &&
+       head -n1 actual | grep master.to.new &&
+       tail -n2 actual | head -n1 | grep first
+'
+
+test_expect_success 'reflog_exists(HEAD)' '
+       $RUN reflog-exists HEAD
+'
+
+test_expect_success 'delete_reflog() not allowed' '
+       test_must_fail $RUN delete-reflog HEAD
+'
+
+test_expect_success 'create-reflog() not allowed' '
+       test_must_fail $RUN create-reflog HEAD 1
+'
+
+test_done
index 33a51c9a67fe833e31e51099f7568b64be385d07..677e15a7a43ba774f7e54486cd8ac4c0565475ff 100755 (executable)
@@ -689,4 +689,17 @@ test_expect_success 'bogus head does not fallback to all heads' '
        ! grep $blob out
 '
 
+test_expect_success 'detect corrupt index file in fsck' '
+       cp .git/index .git/index.backup &&
+       test_when_finished "mv .git/index.backup .git/index" &&
+       echo zzzzzzzz >zzzzzzzz &&
+       git add zzzzzzzz &&
+       sed -e "s/zzzzzzzz/yyyyyyyy/" .git/index >.git/index.yyy &&
+       mv .git/index.yyy .git/index &&
+       # Confirm that fsck detects invalid checksum
+       test_must_fail git fsck --cache &&
+       # Confirm that status no longer complains about invalid checksum
+       git status
+'
+
 test_done
index 46ef1f22dca14423ecff8da45d608a8885f23dba..b23c4e3fab604f957ec6359eae75400dd1e53174 100755 (executable)
@@ -46,11 +46,14 @@ error_message () {
 }
 
 test_expect_success '@{upstream} resolves to correct full name' '
-       test refs/remotes/origin/master = "$(full_name @{upstream})"
+       test refs/remotes/origin/master = "$(full_name @{upstream})" &&
+       test refs/remotes/origin/master = "$(full_name @{UPSTREAM})" &&
+       test refs/remotes/origin/master = "$(full_name @{UpSTReam})"
 '
 
 test_expect_success '@{u} resolves to correct full name' '
-       test refs/remotes/origin/master = "$(full_name @{u})"
+       test refs/remotes/origin/master = "$(full_name @{u})" &&
+       test refs/remotes/origin/master = "$(full_name @{U})"
 '
 
 test_expect_success 'my-side@{upstream} resolves to correct full name' '
@@ -60,6 +63,8 @@ test_expect_success 'my-side@{upstream} resolves to correct full name' '
 test_expect_success 'upstream of branch with @ in middle' '
        full_name fun@ny@{u} >actual &&
        echo refs/remotes/origin/side >expect &&
+       test_cmp expect actual &&
+       full_name fun@ny@{U} >actual &&
        test_cmp expect actual
 '
 
@@ -96,12 +101,14 @@ test_expect_success 'not-tracking@{u} fails' '
 test_expect_success '<branch>@{u}@{1} resolves correctly' '
        test_commit 6 &&
        (cd clone && git fetch) &&
-       test 5 = $(commit_subject my-side@{u}@{1})
+       test 5 = $(commit_subject my-side@{u}@{1}) &&
+       test 5 = $(commit_subject my-side@{U}@{1})
 '
 
 test_expect_success '@{u} without specifying branch fails on a detached HEAD' '
        git checkout HEAD^0 &&
-       test_must_fail git rev-parse @{u}
+       test_must_fail git rev-parse @{u} &&
+       test_must_fail git rev-parse @{U}
 '
 
 test_expect_success 'checkout -b new my-side@{u} forks from the same' '
index 623a32aa6e323d11267cb89af62d42c325a4dd75..788cc91e452ce02af1c068b8c7b0bb1aa88ae395 100755 (executable)
@@ -24,12 +24,16 @@ test_expect_success 'setup' '
 
 test_expect_success '@{push} with default=nothing' '
        test_config push.default nothing &&
-       test_must_fail git rev-parse master@{push}
+       test_must_fail git rev-parse master@{push} &&
+       test_must_fail git rev-parse master@{PUSH} &&
+       test_must_fail git rev-parse master@{PuSH}
 '
 
 test_expect_success '@{push} with default=simple' '
        test_config push.default simple &&
-       resolve master@{push} refs/remotes/origin/master
+       resolve master@{push} refs/remotes/origin/master &&
+       resolve master@{PUSH} refs/remotes/origin/master &&
+       resolve master@{pUSh} refs/remotes/origin/master
 '
 
 test_expect_success 'triangular @{push} fails with default=simple' '
index 6847f7582234d656563f867976a64142a3a01621..e8f70b806f110c75b9bda5bca0af6732be391fcd 100755 (executable)
@@ -63,6 +63,12 @@ test_expect_success '"checkout <submodule>" honors submodule.*.ignore from .git/
        ! test -s actual
 '
 
+KNOWN_FAILURE_DIRECTORY_SUBMODULE_CONFLICTS=1
+KNOWN_FAILURE_SUBMODULE_RECURSIVE_NESTED=1
+test_submodule_switch_recursing "git checkout --recurse-submodules"
+
+test_submodule_forced_switch_recursing "git checkout -f --recurse-submodules"
+
 test_submodule_switch "git checkout"
 
 test_submodule_forced_switch "git checkout -f"
index 848da5f3684ac5382063a64588c49a9128235cf1..720063bf0d09bb8c577b78e099187abfc7da71b0 100755 (executable)
@@ -20,7 +20,7 @@ test_expect_success 'rev-parse --git-common-dir on main worktree' '
 
 test_expect_success 'rev-parse --git-path objects linked worktree' '
        echo "$(git rev-parse --show-toplevel)/.git/objects" >expect &&
-       test_when_finished "rm -rf linked-tree && git worktree prune" &&
+       test_when_finished "rm -rf linked-tree actual expect && git worktree prune" &&
        git worktree add --detach linked-tree master &&
        git -C linked-tree rev-parse --git-path objects >actual &&
        test_cmp expect actual
@@ -28,19 +28,21 @@ test_expect_success 'rev-parse --git-path objects linked worktree' '
 
 test_expect_success '"list" all worktrees from main' '
        echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
-       test_when_finished "rm -rf here && git worktree prune" &&
+       test_when_finished "rm -rf here out actual expect && git worktree prune" &&
        git worktree add --detach here master &&
        echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git worktree list | sed "s/  */ /g" >actual &&
+       git worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
 test_expect_success '"list" all worktrees from linked' '
        echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
-       test_when_finished "rm -rf here && git worktree prune" &&
+       test_when_finished "rm -rf here out actual expect && git worktree prune" &&
        git worktree add --detach here master &&
        echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git -C here worktree list | sed "s/  */ /g" >actual &&
+       git -C here worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
@@ -49,7 +51,7 @@ test_expect_success '"list" all worktrees --porcelain' '
        echo "HEAD $(git rev-parse HEAD)" >>expect &&
        echo "branch $(git symbolic-ref HEAD)" >>expect &&
        echo >>expect &&
-       test_when_finished "rm -rf here && git worktree prune" &&
+       test_when_finished "rm -rf here actual expect && git worktree prune" &&
        git worktree add --detach here master &&
        echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect &&
        echo "HEAD $(git rev-parse HEAD)" >>expect &&
@@ -69,16 +71,17 @@ test_expect_success 'bare repo setup' '
 '
 
 test_expect_success '"list" all worktrees from bare main' '
-       test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+       test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
        git -C bare1 worktree add --detach ../there master &&
        echo "$(pwd)/bare1 (bare)" >expect &&
        echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git -C bare1 worktree list | sed "s/  */ /g" >actual &&
+       git -C bare1 worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
 test_expect_success '"list" all worktrees --porcelain from bare main' '
-       test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+       test_when_finished "rm -rf there actual expect && git -C bare1 worktree prune" &&
        git -C bare1 worktree add --detach ../there master &&
        echo "worktree $(pwd)/bare1" >expect &&
        echo "bare" >>expect &&
@@ -92,11 +95,12 @@ test_expect_success '"list" all worktrees --porcelain from bare main' '
 '
 
 test_expect_success '"list" all worktrees from linked with a bare main' '
-       test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+       test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
        git -C bare1 worktree add --detach ../there master &&
        echo "$(pwd)/bare1 (bare)" >expect &&
        echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git -C there worktree list | sed "s/  */ /g" >actual &&
+       git -C there worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
@@ -118,9 +122,11 @@ test_expect_success 'broken main worktree still at the top' '
                cd linked &&
                echo "worktree $(pwd)" >expected &&
                echo "ref: .broken" >../.git/HEAD &&
-               git worktree list --porcelain | head -n 3 >actual &&
+               git worktree list --porcelain >out &&
+               head -n 3 out >actual &&
                test_cmp ../expected actual &&
-               git worktree list | head -n 1 >actual.2 &&
+               git worktree list >out &&
+               head -n 1 out >actual.2 &&
                grep -F "(error)" actual.2
        )
 '
@@ -134,7 +140,8 @@ test_expect_success 'linked worktrees are sorted' '
                test_commit new &&
                git worktree add ../first &&
                git worktree add ../second &&
-               git worktree list --porcelain | grep ^worktree >actual
+               git worktree list --porcelain >out &&
+               grep ^worktree out >actual
        ) &&
        cat >expected <<-EOF &&
        worktree $(pwd)/sorted/main
index a5426171d3ec652ffc02fc8279bc5802f57204a3..ebb956fd16ccce2536591da021239453da999140 100755 (executable)
@@ -77,10 +77,22 @@ test_expect_success 'ls-files recurses more than 1 level' '
        git -C submodule/subsub commit -m "add d" &&
        git -C submodule submodule add ./subsub &&
        git -C submodule commit -m "added subsub" &&
+       git submodule absorbgitdirs &&
        git ls-files --recurse-submodules >actual &&
        test_cmp expect actual
 '
 
+test_expect_success 'ls-files works with GIT_DIR' '
+       cat >expect <<-\EOF &&
+       .gitmodules
+       c
+       subsub/d
+       EOF
+
+       git --git-dir=submodule/.git ls-files --recurse-submodules >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '--recurse-submodules and pathspecs setup' '
        echo e >submodule/subsub/e.txt &&
        git -C submodule/subsub add e.txt &&
@@ -188,6 +200,45 @@ test_expect_success '--recurse-submodules and pathspecs' '
        test_cmp expect actual
 '
 
+test_expect_success '--recurse-submodules and relative paths' '
+       # From subdir
+       cat >expect <<-\EOF &&
+       b
+       EOF
+       git -C b ls-files --recurse-submodules >actual &&
+       test_cmp expect actual &&
+
+       # Relative path to top
+       cat >expect <<-\EOF &&
+       ../.gitmodules
+       ../a
+       b
+       ../h.txt
+       ../sib/file
+       ../sub/file
+       ../submodule/.gitmodules
+       ../submodule/c
+       ../submodule/f.TXT
+       ../submodule/g.txt
+       ../submodule/subsub/d
+       ../submodule/subsub/e.txt
+       EOF
+       git -C b ls-files --recurse-submodules -- .. >actual &&
+       test_cmp expect actual &&
+
+       # Relative path to submodule
+       cat >expect <<-\EOF &&
+       ../submodule/.gitmodules
+       ../submodule/c
+       ../submodule/f.TXT
+       ../submodule/g.txt
+       ../submodule/subsub/d
+       ../submodule/subsub/e.txt
+       EOF
+       git -C b ls-files --recurse-submodules -- ../submodule >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '--recurse-submodules does not support --error-unmatch' '
        test_must_fail git ls-files --recurse-submodules --error-unmatch 2>actual &&
        test_i18ngrep "does not support --error-unmatch" actual
diff --git a/t/t3008-ls-files-lazy-init-name-hash.sh b/t/t3008-ls-files-lazy-init-name-hash.sh
new file mode 100755 (executable)
index 0000000..bdf5198
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+test_description='Test the lazy init name hash with various folder structures'
+
+. ./test-lib.sh
+
+if test 1 -eq $($GIT_BUILD_DIR/t/helper/test-online-cpus)
+then
+       skip_all='skipping lazy-init tests, single cpu'
+       test_done
+fi
+
+LAZY_THREAD_COST=2000
+
+test_expect_success 'no buffer overflow in lazy_init_name_hash' '
+       (
+           test_seq $LAZY_THREAD_COST | sed "s/^/a_/"
+           echo b/b/b
+           test_seq $LAZY_THREAD_COST | sed "s/^/c_/"
+           test_seq 50 | sed "s/^/d_/" | tr "\n" "/"; echo d
+       ) |
+       sed "s/^/100644 $EMPTY_BLOB     /" |
+       git update-index --index-info &&
+       test-lazy-init-name-hash -m
+'
+
+test_done
index 9f353c0efce82e4492b0f3ce3acce1756827462e..fe62e7c775da6a8fc191ed1dc6634d5285ddbc4b 100755 (executable)
@@ -978,6 +978,10 @@ test_expect_success '--merged catches invalid object names' '
        test_must_fail git branch --merged 0000000000000000000000000000000000000000
 '
 
+test_expect_success '--merged is incompatible with --no-merged' '
+       test_must_fail git branch --merged HEAD --no-merged HEAD
+'
+
 test_expect_success 'tracking with unexpected .fetch refspec' '
        rm -rf a b c d &&
        git init a &&
index 7f3ec47241cd6af7a1c107cba114decaa87df108..0ef1b6fdcc420d6fac2cbe0abd5f1ec6ebec90c6 100755 (executable)
@@ -1,6 +1,6 @@
 #!/bin/sh
 
-test_description='branch --contains <commit>, --merged, and --no-merged'
+test_description='branch --contains <commit>, --no-contains <commit> --merged, and --no-merged'
 
 . ./test-lib.sh
 
@@ -45,6 +45,22 @@ test_expect_success 'branch --contains master' '
 
 '
 
+test_expect_success 'branch --no-contains=master' '
+
+       git branch --no-contains=master >actual &&
+       >expect &&
+       test_cmp expect actual
+
+'
+
+test_expect_success 'branch --no-contains master' '
+
+       git branch --no-contains master >actual &&
+       >expect &&
+       test_cmp expect actual
+
+'
+
 test_expect_success 'branch --contains=side' '
 
        git branch --contains=side >actual &&
@@ -55,6 +71,16 @@ test_expect_success 'branch --contains=side' '
 
 '
 
+test_expect_success 'branch --no-contains=side' '
+
+       git branch --no-contains=side >actual &&
+       {
+               echo "  master"
+       } >expect &&
+       test_cmp expect actual
+
+'
+
 test_expect_success 'branch --contains with pattern implies --list' '
 
        git branch --contains=master master >actual &&
@@ -65,6 +91,14 @@ test_expect_success 'branch --contains with pattern implies --list' '
 
 '
 
+test_expect_success 'branch --no-contains with pattern implies --list' '
+
+       git branch --no-contains=master master >actual &&
+       >expect &&
+       test_cmp expect actual
+
+'
+
 test_expect_success 'side: branch --merged' '
 
        git branch --merged >actual &&
@@ -126,8 +160,20 @@ test_expect_success 'branch --no-merged with pattern implies --list' '
 test_expect_success 'implicit --list conflicts with modification options' '
 
        test_must_fail git branch --contains=master -d &&
-       test_must_fail git branch --contains=master -m foo
+       test_must_fail git branch --contains=master -m foo &&
+       test_must_fail git branch --no-contains=master -d &&
+       test_must_fail git branch --no-contains=master -m foo
+
+'
 
+test_expect_success 'Assert that --contains only works on commits, not trees & blobs' '
+       test_must_fail git branch --contains master^{tree} &&
+       blob=$(git hash-object -w --stdin <<-\EOF
+       Some blob
+       EOF
+       ) &&
+       test_must_fail git branch --contains $blob &&
+       test_must_fail git branch --no-contains $blob
 '
 
 # We want to set up a case where the walk for the tracking info
@@ -159,4 +205,15 @@ test_expect_success 'branch --merged with --verbose' '
        test_i18ncmp expect actual
 '
 
+test_expect_success 'branch --contains combined with --no-contains' '
+       git branch --contains zzz --no-contains topic >actual &&
+       cat >expect <<-\EOF &&
+         master
+         side
+         zzz
+       EOF
+       test_cmp expect actual
+
+'
+
 test_done
diff --git a/t/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
new file mode 100755 (executable)
index 0000000..2afb564
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='git rebase --signoff
+
+This test runs git rebase --signoff and make sure that it works.
+'
+
+. ./test-lib.sh
+
+# A simple file to commit
+cat >file <<EOF
+a
+EOF
+
+# Expected commit message after rebase --signoff
+cat >expected-signed <<EOF
+first
+
+Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
+EOF
+
+# Expected commit message after rebase without --signoff (or with --no-signoff)
+cat >expected-unsigned <<EOF
+first
+EOF
+
+
+# We configure an alias to do the rebase --signoff so that
+# on the next subtest we can show that --no-signoff overrides the alias
+test_expect_success 'rebase --signoff adds a sign-off line' '
+       git commit --allow-empty -m "Initial empty commit" &&
+       git add file && git commit -m first &&
+       git config alias.rbs "rebase --signoff" &&
+       git rbs HEAD^ &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+       test_cmp expected-signed actual
+'
+
+test_expect_success 'rebase --no-signoff does not add a sign-off line' '
+       git commit --amend -m "first" &&
+       git rbs --no-signoff HEAD^ &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+       test_cmp expected-unsigned actual
+'
+
+test_done
index e37547f41a21ebda150aad6797012c9df3742382..b1602718f85468d17faa1cc0d7ae8854ac2f5407 100755 (executable)
@@ -31,6 +31,15 @@ test_expect_success setup '
 
 '
 
+test_expect_success 'cherry-pick -m complains of bogus numbers' '
+       # expect 129 here to distinguish between cases where
+       # there was nothing to cherry-pick
+       test_expect_code 129 git cherry-pick -m &&
+       test_expect_code 129 git cherry-pick -m foo b &&
+       test_expect_code 129 git cherry-pick -m -1 b &&
+       test_expect_code 129 git cherry-pick -m 0 b
+'
+
 test_expect_success 'cherry-pick a non-merge with -m should fail' '
 
        git reset --hard &&
index 5aa6db584cd0dbddf6060eeb46f4c1c9836bc935..5f9913ba33d3edc848b03fc37bed587fe5c54849 100755 (executable)
@@ -268,6 +268,14 @@ cat >expect.modified <<EOF
  M submod
 EOF
 
+cat >expect.modified_inside <<EOF
+ m submod
+EOF
+
+cat >expect.modified_untracked <<EOF
+ ? submod
+EOF
+
 cat >expect.cached <<EOF
 D  submod
 EOF
@@ -421,7 +429,7 @@ test_expect_success 'rm of a populated submodule with modifications fails unless
        test -d submod &&
        test -f submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test_cmp expect.modified actual &&
+       test_cmp expect.modified_inside actual &&
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none >actual &&
@@ -436,7 +444,7 @@ test_expect_success 'rm of a populated submodule with untracked files fails unle
        test -d submod &&
        test -f submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test_cmp expect.modified actual &&
+       test_cmp expect.modified_untracked actual &&
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none >actual &&
@@ -621,7 +629,7 @@ test_expect_success 'rm of a populated nested submodule with different nested HE
        test -d submod &&
        test -f submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test_cmp expect.modified actual &&
+       test_cmp expect.modified_inside actual &&
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none >actual &&
@@ -636,7 +644,7 @@ test_expect_success 'rm of a populated nested submodule with nested modification
        test -d submod &&
        test -f submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test_cmp expect.modified actual &&
+       test_cmp expect.modified_inside actual &&
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none >actual &&
@@ -651,14 +659,14 @@ test_expect_success 'rm of a populated nested submodule with nested untracked fi
        test -d submod &&
        test -f submod/.git &&
        git status -s -uno --ignore-submodules=none >actual &&
-       test_cmp expect.modified actual &&
+       test_cmp expect.modified_untracked actual &&
        git rm -f submod &&
        test ! -d submod &&
        git status -s -uno --ignore-submodules=none >actual &&
        test_cmp expect actual
 '
 
-test_expect_success 'rm of a populated nested submodule with a nested .git directory fails even when forced' '
+test_expect_success "rm absorbs submodule's nested .git directory" '
        git reset --hard &&
        git submodule update --recursive &&
        (cd submod/subsubmod &&
index 89877e4b52a4aa09b1e1925005a1a09826e2c7a0..b71d1e659e97c0f34bc5bcfeda65b854c2d714e9 100755 (executable)
@@ -663,7 +663,7 @@ test_expect_success 'stash apply shows status same as git status (relative to cu
                sane_unset GIT_MERGE_VERBOSITY &&
                git stash apply
        ) |
-       sed -e 1,2d >actual && # drop "Saved..." and "HEAD is now..."
+       sed -e 1d >actual && # drop "Saved..."
        test_i18ncmp expect actual
 '
 
@@ -907,4 +907,18 @@ test_expect_success 'stash without verb with pathspec' '
        test_path_is_file bar
 '
 
+test_expect_success 'stash -k -- <pathspec> leaves unstaged files intact' '
+       git reset &&
+       >foo &&
+       >bar &&
+       git add foo bar &&
+       git commit -m "test" &&
+       echo "foo" >foo &&
+       echo "bar" >bar &&
+       git stash -k -- foo &&
+       test "",bar = $(cat foo),$(cat bar) &&
+       git stash pop &&
+       test foo,bar = $(cat foo),$(cat bar)
+'
+
 test_done
index 38e730090fe311ea82c737646aedba214d1143bb..83744f8c930637560d7d97123a2ffbd38030637e 100755 (executable)
@@ -77,6 +77,14 @@ test_expect_success 'git stash --no-keep-index -p' '
        verify_state dir/foo work index
 '
 
+test_expect_success 'stash -p --no-keep-index -- <pathspec> does not unstage other files' '
+       set_state HEAD HEADfile_work HEADfile_index &&
+       set_state dir/foo work index &&
+       echo y | git stash push -p --no-keep-index -- HEAD &&
+       verify_state HEAD committed committed &&
+       verify_state dir/foo work index
+'
+
 test_expect_success 'none of this moved HEAD' '
        verify_saved_head
 '
index 7e23b55ea4c5d8ddc11e74381083a1757c4553f8..d4a3ffa69cd2e88b19e05149d2e752bec89ee57c 100755 (executable)
@@ -746,4 +746,33 @@ test_expect_success 'diff --submodule=diff with .git file' '
        test_cmp expected actual
 '
 
+test_expect_success 'setup nested submodule' '
+       git submodule add -f ./sm2 &&
+       git commit -a -m "add sm2" &&
+       git -C sm2 submodule add ../sm2 nested &&
+       git -C sm2 commit -a -m "nested sub"
+'
+
+test_expect_success 'move nested submodule HEAD' '
+       echo "nested content" >sm2/nested/file &&
+       git -C sm2/nested add file &&
+       git -C sm2/nested commit --allow-empty -m "new HEAD"
+'
+
+test_expect_success 'diff --submodule=diff with moved nested submodule HEAD' '
+       cat >expected <<-EOF &&
+       Submodule nested a5a65c9..b55928c:
+       diff --git a/nested/file b/nested/file
+       new file mode 100644
+       index 0000000..ca281f5
+       --- /dev/null
+       +++ b/nested/file
+       @@ -0,0 +1 @@
+       +nested content
+       EOF
+       git -C sm2 diff --submodule=diff >actual 2>err &&
+       test_must_be_empty err &&
+       test_cmp expected actual
+'
+
 test_done
index f0bf50bda780f04f9f2ffc2c1f39e354f69ae193..7c4903f49713a22d7fba28a608acf07f1330110b 100755 (executable)
@@ -19,4 +19,9 @@ test_expect_success '-G matches' '
        test 4096-zeroes.txt = "$(cat out)"
 '
 
+test_expect_success '-S --pickaxe-regex' '
+       git diff --name-only -S0 --pickaxe-regex HEAD^ >out &&
+       verbose test 4096-zeroes.txt = "$(cat out)"
+'
+
 test_done
index 89a5bacac5ad069ec10b16bfdf3dbde7217cf4de..44807e218d7016f58bd41b89af71104a37f31a8b 100755 (executable)
@@ -983,7 +983,9 @@ test_expect_success 'am works with multi-line in-body headers' '
        rm -fr .git/rebase-apply &&
        git checkout -f first &&
        echo one >> file &&
-       git commit -am "$LONG" --author="$LONG <long@example.com>" &&
+       git commit -am "$LONG
+
+    Body test" --author="$LONG <long@example.com>" &&
        git format-patch --stdout -1 >patch &&
        # bump from, date, and subject down to in-body header
        perl -lpe "
@@ -997,7 +999,7 @@ test_expect_success 'am works with multi-line in-body headers' '
        git am msg &&
        # Ensure that the author and full message are present
        git cat-file commit HEAD | grep "^author.*long@example.com" &&
-       git cat-file commit HEAD | grep "^$LONG"
+       git cat-file commit HEAD | grep "^$LONG$"
 '
 
 test_done
index 48b55bfd274a7adffe18af13489a54a7d92707a4..f577990716011cfbc9a1b605aa5f23fe0d660c56 100755 (executable)
@@ -4,6 +4,7 @@ test_description='git log'
 
 . ./test-lib.sh
 . "$TEST_DIRECTORY/lib-gpg.sh"
+. "$TEST_DIRECTORY/lib-terminal.sh"
 
 test_expect_success setup '
 
@@ -520,7 +521,7 @@ test_expect_success 'log --graph with merge' '
 '
 
 test_expect_success 'log.decorate configuration' '
-       git log --oneline >expect.none &&
+       git log --oneline --no-decorate >expect.none &&
        git log --oneline --decorate >expect.short &&
        git log --oneline --decorate=full >expect.full &&
 
@@ -576,6 +577,13 @@ test_expect_success 'log.decorate configuration' '
 
 '
 
+test_expect_success TTY 'log output on a TTY' '
+       git log --oneline --decorate >expect.short &&
+
+       test_terminal git log --oneline >actual &&
+       test_cmp expect.short actual
+'
+
 test_expect_success 'reflog is expected format' '
        git log -g --abbrev-commit --pretty=oneline >expect &&
        git reflog >actual &&
index f55137f76ffac4c6f8333444b444aef65da8b3fd..57ba3226288cadbbe8af569d13217f5b2c17af90 100755 (executable)
@@ -475,4 +475,56 @@ test_expect_success 'push only unpushed submodules recursively' '
        test_cmp expected_pub actual_pub
 '
 
+test_expect_success 'push propagating the remotes name to a submodule' '
+       git -C work remote add origin ../pub.git &&
+       git -C work remote add pub ../pub.git &&
+
+       > work/gar/bage/junk10 &&
+       git -C work/gar/bage add junk10 &&
+       git -C work/gar/bage commit -m "Tenth junk" &&
+       git -C work add gar/bage &&
+       git -C work commit -m "Tenth junk added to gar/bage" &&
+
+       # Fails when submodule does not have a matching remote
+       test_must_fail git -C work push --recurse-submodules=on-demand pub master &&
+       # Succeeds when submodules has matching remote and refspec
+       git -C work push --recurse-submodules=on-demand origin master &&
+
+       git -C submodule.git rev-parse master >actual_submodule &&
+       git -C pub.git rev-parse master >actual_pub &&
+       git -C work/gar/bage rev-parse master >expected_submodule &&
+       git -C work rev-parse master >expected_pub &&
+       test_cmp expected_submodule actual_submodule &&
+       test_cmp expected_pub actual_pub
+'
+
+test_expect_success 'push propagating refspec to a submodule' '
+       > work/gar/bage/junk11 &&
+       git -C work/gar/bage add junk11 &&
+       git -C work/gar/bage commit -m "Eleventh junk" &&
+
+       git -C work checkout branch2 &&
+       git -C work add gar/bage &&
+       git -C work commit -m "updating gar/bage in branch2" &&
+
+       # Fails when submodule does not have a matching branch
+       test_must_fail git -C work push --recurse-submodules=on-demand origin branch2 &&
+       # Fails when refspec includes an object id
+       test_must_fail git -C work push --recurse-submodules=on-demand origin \
+               "$(git -C work rev-parse branch2):refs/heads/branch2" &&
+       # Fails when refspec includes 'HEAD' as it is unsupported at this time
+       test_must_fail git -C work push --recurse-submodules=on-demand origin \
+               HEAD:refs/heads/branch2 &&
+
+       git -C work/gar/bage branch branch2 master &&
+       git -C work push --recurse-submodules=on-demand origin branch2 &&
+
+       git -C submodule.git rev-parse branch2 >actual_submodule &&
+       git -C pub.git rev-parse branch2 >actual_pub &&
+       git -C work/gar/bage rev-parse branch2 >expected_submodule &&
+       git -C work rev-parse branch2 >expected_pub &&
+       test_cmp expected_submodule actual_submodule &&
+       test_cmp expected_pub actual_pub
+'
+
 test_done
index 9a57a7d8f2319e48a267e4f2a605e420f779fd96..f9232f5d0ffff996359a27d5727f645f8fcff6d3 100755 (executable)
@@ -102,17 +102,86 @@ 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 remote helper' '\
+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 &&
+       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" &&
+       mk_repo_pair &&
+       git -C upstream config receive.advertisePushOptions true &&
+       cp -r upstream parent_upstream &&
+       test_commit -C upstream one &&
+
+       test_create_repo parent &&
+       git -C parent remote add up ../parent_upstream &&
+       test_commit -C parent one &&
+       git -C parent push --mirror up &&
+
+       git -C parent submodule add ../upstream workbench &&
+       git -C parent/workbench remote add up ../../upstream &&
+       git -C parent commit -m "add submoule" &&
+
+       test_commit -C parent/workbench two &&
+       git -C parent add workbench &&
+       git -C parent commit -m "update workbench" &&
+
+       git -C parent push \
+               --push-option=asdf --push-option="more structured text" \
+               --recurse-submodules=on-demand up master &&
+
+       git -C upstream rev-parse --verify master >expect &&
+       git -C parent/workbench rev-parse --verify master >actual &&
+       test_cmp expect actual &&
+
+       git -C parent_upstream rev-parse --verify master >expect &&
+       git -C parent rev-parse --verify master >actual &&
+       test_cmp expect actual &&
+
+       printf "asdf\nmore structured text\n" >expect &&
+       test_cmp expect upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect upstream/.git/hooks/post-receive.push_options &&
+       test_cmp expect parent_upstream/.git/hooks/pre-receive.push_options &&
+       test_cmp expect parent_upstream/.git/hooks/post-receive.push_options
+'
+
 stop_httpd
 
 test_done
index af9fcd833a5e9e4997dd5cb917ac13f4b07f10ab..113c87007f31abced0d93b3702f0b54f50ff4679 100755 (executable)
@@ -58,4 +58,15 @@ test_expect_success 'push to repo path with path separator (colon)' '
        git push "$(pwd)/xxx${pathsep}yyy.git" HEAD
 '
 
+test_expect_success 'updating a ref from quarantine is forbidden' '
+       git init --bare update.git &&
+       write_script update.git/hooks/pre-receive <<-\EOF &&
+       read old new refname
+       git update-ref refs/heads/unrelated $new
+       exit 1
+       EOF
+       test_must_fail git push update.git HEAD &&
+       git -C update.git fsck
+'
+
 test_done
index b52b8acf9859b0084ea10ebf184ed95cafb0b2d8..9c56f771b619e4bb931ce3d34a5f9325b8f61e90 100755 (executable)
@@ -427,6 +427,12 @@ test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' '
        expect_ssh "-batch -P 123" myhost src
 '
 
+test_expect_success 'clean failure on broken quoting' '
+       test_must_fail \
+               env GIT_SSH_COMMAND="${SQ}plink.exe -v" \
+               git clone "[myhost:123]:src" sq-failure
+'
+
 # Reset the GIT_SSH environment variable for clone tests.
 setup_ssh_wrapper
 
index 26ebb0375deb67be38974c7c5610f090a5a2a9aa..d2d883f3a1d473a2b69fab05adbb21e67e5e600c 100755 (executable)
@@ -77,6 +77,7 @@ test_expect_success 'mix of quoted and unquoted alternates' '
        check_obj "$quoted:$unquoted" <<-EOF
        $one blob
        $two blob
+       EOF
 '
 
 test_expect_success !MINGW 'broken quoting falls back to interpreting raw' '
index 167491fd5b0d0c5ed3744c51cd0f05723c9f964b..16952e44fcfd705e115b81099bdbff809ff1520d 100755 (executable)
@@ -233,4 +233,24 @@ test_expect_success 'describe --contains and --no-match' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup and absorb a submodule' '
+       test_create_repo sub1 &&
+       test_commit -C sub1 initial &&
+       git submodule add ./sub1 &&
+       git submodule absorbgitdirs &&
+       git commit -a -m "add submodule" &&
+       git describe --dirty >expect &&
+       git describe --broken >out &&
+       test_cmp expect out
+'
+
+test_expect_success 'describe chokes on severly broken submodules' '
+       mv .git/modules/sub1/ .git/modules/sub_moved &&
+       test_must_fail git describe --dirty
+'
+test_expect_success 'describe ignoring a borken submodule' '
+       git describe --broken >out &&
+       grep broken out
+'
+
 test_done
index a09a1a46eff5357c3939f5715f4a623effcb6428..fc067ed6723bc0aaaddd2473e4d61f13087b9e15 100755 (executable)
@@ -93,6 +93,22 @@ test_expect_success 'filtering with --contains' '
        test_cmp expect actual
 '
 
+test_expect_success 'filtering with --no-contains' '
+       cat >expect <<-\EOF &&
+       refs/tags/one
+       EOF
+       git for-each-ref --format="%(refname)" --no-contains=two >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'filtering with --contains and --no-contains' '
+       cat >expect <<-\EOF &&
+       refs/tags/two
+       EOF
+       git for-each-ref --format="%(refname)" --contains=two --no-contains=three >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success '%(color) must fail' '
        test_must_fail git for-each-ref --format="%(color)%(refname)"
 '
@@ -421,4 +437,8 @@ test_expect_success 'check %(if:notequals=<string>)' '
        test_cmp expect actual
 '
 
+test_expect_success '--merged is incompatible with --no-merged' '
+       test_must_fail git for-each-ref --merged HEAD --no-merged HEAD
+'
+
 test_done
index 08de2e8ab09435c535f8aab5b455a074453e91a8..cc7acd101db082302b351a81929f3f571c1a4db0 100755 (executable)
@@ -67,6 +67,16 @@ test_expect_success 'auto gc with too many loose objects does not attempt to cre
        test_line_count = 2 new # There is one new pack and its .idx
 '
 
+run_and_wait_for_auto_gc () {
+       # We read stdout from gc for the side effect of waiting until the
+       # background gc process exits, closing its fd 9.  Furthermore, the
+       # variable assignment from a command substitution preserves the
+       # exit status of the main gc process.
+       # Note: this fd trickery doesn't work on Windows, but there is no
+       # need to, because on Win the auto gc always runs in the foreground.
+       doesnt_matter=$(git gc --auto 9>&1)
+}
+
 test_expect_success 'background auto gc does not run if gc.log is present and recent but does if it is old' '
        test_commit foo &&
        test_commit bar &&
@@ -80,7 +90,13 @@ test_expect_success 'background auto gc does not run if gc.log is present and re
        test-chmtime =-345600 .git/gc.log &&
        test_must_fail git gc --auto &&
        test_config gc.logexpiry 2.days &&
-       git gc --auto
+       run_and_wait_for_auto_gc &&
+       ls .git/objects/pack/pack-*.pack >packs &&
+       test_line_count = 1 packs
 '
 
+# DO NOT leave a detached auto gc process running near the end of the
+# test script: it can run long enough in the background to racily
+# interfere with the cleanup in 'test_done'.
+
 test_done
index b4698ab5f53c2ce92bd349fe4fa336e5fc5ca3e5..bb2e4d704de006025ae0db9369b81bbebc15750a 100755 (executable)
@@ -16,7 +16,6 @@ tag_exists () {
        git show-ref --quiet --verify refs/tags/"$1"
 }
 
-# todo: git tag -l now returns always zero, when fixed, change this test
 test_expect_success 'listing all tags in an empty tree should succeed' '
        git tag -l &&
        git tag
@@ -119,6 +118,18 @@ test_expect_success 'listing all tags if one exists should succeed' '
        git tag
 '
 
+cat >expect <<EOF
+mytag
+EOF
+test_expect_success 'Multiple -l or --list options are equivalent to one -l option' '
+       git tag -l -l >actual &&
+       test_cmp expect actual &&
+       git tag --list --list >actual &&
+       test_cmp expect actual &&
+       git tag --list -l --list >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'listing all tags if one exists should output that tag' '
        test $(git tag -l) = mytag &&
        test $(git tag) = mytag
@@ -136,9 +147,8 @@ test_expect_success \
        'listing a tag using a matching pattern should output that tag' \
        'test $(git tag -l mytag) = mytag'
 
-# todo: git tag -l now returns always zero, when fixed, change this test
 test_expect_success \
-       'listing tags using a non-matching pattern should suceed' \
+       'listing tags using a non-matching pattern should succeed' \
        'git tag -l xxx'
 
 test_expect_success \
@@ -338,6 +348,19 @@ test_expect_success 'tag -l can accept multiple patterns' '
        test_cmp expect actual
 '
 
+# Between v1.7.7 & v2.13.0 a fair reading of the git-tag documentation
+# could leave you with the impression that "-l <pattern> -l <pattern>"
+# was how we wanted to accept multiple patterns.
+#
+# This test should not imply that this is a sane thing to support. but
+# since the documentation was worded like it was let's at least find
+# out if we're going to break this long-documented form of taking
+# multiple patterns.
+test_expect_success 'tag -l <pattern> -l <pattern> works, as our buggy documentation previously suggested' '
+       git tag -l "v1*" -l "v0*" >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'listing tags in column' '
        COLUMNS=40 git tag -l --column=row >actual &&
        cat >expected <<\EOF &&
@@ -620,6 +643,11 @@ test_expect_success \
        git tag -n0 -l tag-one-line >actual &&
        test_cmp expect actual &&
 
+       git tag -n0 | grep "^tag-one-line" >actual &&
+       test_cmp expect actual &&
+       git tag -n0 tag-one-line >actual &&
+       test_cmp expect actual &&
+
        echo "tag-one-line    A msg" >expect &&
        git tag -n1 -l | grep "^tag-one-line" >actual &&
        test_cmp expect actual &&
@@ -633,6 +661,17 @@ test_expect_success \
        test_cmp expect actual
 '
 
+test_expect_success 'The -n 100 invocation means -n --list 100, not -n100' '
+       >expect &&
+       git tag -n 100 >actual &&
+       test_cmp expect actual &&
+
+       git tag -m "A msg" 100 &&
+       echo "100             A msg" >expect &&
+       git tag -n 100 >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success \
        'listing the zero-lines message of a non-signed tag should succeed' '
        git tag -m "" tag-zero-lines &&
@@ -895,18 +934,16 @@ test_expect_success GPG 'verifying a forged tag should fail' '
        test_must_fail git tag -v forged-tag
 '
 
-test_expect_success 'verifying a proper tag with --format pass and format accordingly' '
-       cat >expect <<-\EOF
+test_expect_success GPG 'verifying a proper tag with --format pass and format accordingly' '
+       cat >expect <<-\EOF &&
        tagname : signed-tag
-       EOF &&
+       EOF
        git tag -v --format="tagname : %(tag)" "signed-tag" >actual &&
        test_cmp expect actual
 '
 
-test_expect_success 'verifying a forged tag with --format fail and format accordingly' '
-       cat >expect <<-\EOF
-       tagname : forged-tag
-       EOF &&
+test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
+       >expect &&
        test_must_fail git tag -v --format="tagname : %(tag)" "forged-tag" >actual &&
        test_cmp expect actual
 '
@@ -1385,6 +1422,23 @@ test_expect_success 'checking that first commit is in all tags (relative)' "
        test_cmp expected actual
 "
 
+# All the --contains tests above, but with --no-contains
+test_expect_success 'checking that first commit is not listed in any tag with --no-contains  (hash)' "
+       >expected &&
+       git tag -l --no-contains $hash1 v* >actual &&
+       test_cmp expected actual
+"
+
+test_expect_success 'checking that first commit is in all tags (tag)' "
+       git tag -l --no-contains v1.0 v* >actual &&
+       test_cmp expected actual
+"
+
+test_expect_success 'checking that first commit is in all tags (relative)' "
+       git tag -l --no-contains HEAD~2 v* >actual &&
+       test_cmp expected actual
+"
+
 cat > expected <<EOF
 v2.0
 EOF
@@ -1394,6 +1448,17 @@ test_expect_success 'checking that second commit only has one tag' "
        test_cmp expected actual
 "
 
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+EOF
+
+test_expect_success 'inverse of the last test, with --no-contains' "
+       git tag -l --no-contains $hash2 v* >actual &&
+       test_cmp expected actual
+"
 
 cat > expected <<EOF
 EOF
@@ -1403,6 +1468,19 @@ test_expect_success 'checking that third commit has no tags' "
        test_cmp expected actual
 "
 
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+EOF
+
+test_expect_success 'conversely --no-contains on the third commit lists all tags' "
+       git tag -l --no-contains $hash3 v* >actual &&
+       test_cmp expected actual
+"
+
 # how about a simple merge?
 
 test_expect_success 'creating simple branch' '
@@ -1424,6 +1502,19 @@ test_expect_success 'checking that branch head only has one tag' "
        test_cmp expected actual
 "
 
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+EOF
+
+test_expect_success 'checking that branch head with --no-contains lists all but one tag' "
+       git tag -l --no-contains $hash4 v* >actual &&
+       test_cmp expected actual
+"
+
 test_expect_success 'merging original branch into this branch' '
        git merge --strategy=ours master &&
         git tag v4.0
@@ -1438,6 +1529,20 @@ test_expect_success 'checking that original branch head has one tag now' "
        test_cmp expected actual
 "
 
+cat > expected <<EOF
+v0.2.1
+v1.0
+v1.0.1
+v1.1.3
+v2.0
+v3.0
+EOF
+
+test_expect_success 'checking that original branch head with --no-contains lists all but one tag now' "
+       git tag -l --no-contains $hash3 v* >actual &&
+       test_cmp expected actual
+"
+
 cat > expected <<EOF
 v0.2.1
 v1.0
@@ -1453,21 +1558,76 @@ test_expect_success 'checking that initial commit is in all tags' "
        test_cmp expected actual
 "
 
+test_expect_success 'checking that --contains can be used in non-list mode' '
+       git tag --contains $hash1 v* >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'checking that initial commit is in all tags with --no-contains' "
+       >expected &&
+       git tag -l --no-contains $hash1 v* >actual &&
+       test_cmp expected actual
+"
+
 # mixing modes and options:
 
 test_expect_success 'mixing incompatibles modes and options is forbidden' '
        test_must_fail git tag -a &&
+       test_must_fail git tag -a -l &&
+       test_must_fail git tag -s &&
+       test_must_fail git tag -s -l &&
+       test_must_fail git tag -m &&
+       test_must_fail git tag -m -l &&
+       test_must_fail git tag -m "hlagh" &&
+       test_must_fail git tag -m "hlagh" -l &&
+       test_must_fail git tag -F &&
+       test_must_fail git tag -F -l &&
+       test_must_fail git tag -f &&
+       test_must_fail git tag -f -l &&
+       test_must_fail git tag -a -s -m -F &&
+       test_must_fail git tag -a -s -m -F -l &&
        test_must_fail git tag -l -v &&
-       test_must_fail git tag -n 100 &&
+       test_must_fail git tag -l -d &&
+       test_must_fail git tag -l -v -d &&
+       test_must_fail git tag -n 100 -v &&
        test_must_fail git tag -l -m msg &&
        test_must_fail git tag -l -F some file &&
-       test_must_fail git tag -v -s
-'
+       test_must_fail git tag -v -s &&
+       test_must_fail git tag --contains tag-tree &&
+       test_must_fail git tag --contains tag-blob &&
+       test_must_fail git tag --no-contains tag-tree &&
+       test_must_fail git tag --no-contains tag-blob &&
+       test_must_fail git tag --contains --no-contains &&
+       test_must_fail git tag --no-with HEAD &&
+       test_must_fail git tag --no-without HEAD
+'
+
+for option in --contains --with --no-contains --without --merged --no-merged --points-at
+do
+       test_expect_success "mixing incompatible modes with $option is forbidden" "
+               test_must_fail git tag -d $option HEAD &&
+               test_must_fail git tag -d $option HEAD some-tag &&
+               test_must_fail git tag -v $option HEAD
+       "
+       test_expect_success "Doing 'git tag --list-like $option <commit> <pattern> is permitted" "
+               git tag -n $option HEAD HEAD &&
+               git tag $option HEAD HEAD &&
+               git tag $option
+       "
+done
 
 # check points-at
 
-test_expect_success '--points-at cannot be used in non-list mode' '
-       test_must_fail git tag --points-at=v4.0 foo
+test_expect_success '--points-at can be used in non-list mode' '
+       echo v4.0 >expect &&
+       git tag --points-at=v4.0 "v*" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--points-at is a synonym for --points-at HEAD' '
+       echo v4.0 >expect &&
+       git tag --points-at >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success '--points-at finds lightweight tags' '
@@ -1709,7 +1869,7 @@ run_with_limited_stack () {
 test_lazy_prereq ULIMIT_STACK_SIZE 'run_with_limited_stack true'
 
 # we require ulimit, this excludes Windows
-test_expect_success ULIMIT_STACK_SIZE '--contains works in a deep repo' '
+test_expect_success ULIMIT_STACK_SIZE '--contains and --no-contains work in a deep repo' '
        >expect &&
        i=1 &&
        while test $i -lt 8000
@@ -1725,7 +1885,9 @@ EOF"
        git checkout master &&
        git tag far-far-away HEAD^ &&
        run_with_limited_stack git tag --contains HEAD >actual &&
-       test_cmp expect actual
+       test_cmp expect actual &&
+       run_with_limited_stack git tag --no-contains HEAD >actual &&
+       test_line_count ">" 10 actual
 '
 
 test_expect_success '--format should list tags as per format given' '
@@ -1744,8 +1906,17 @@ test_expect_success 'setup --merged test tags' '
        git tag mergetest-3 HEAD
 '
 
-test_expect_success '--merged cannot be used in non-list mode' '
-       test_must_fail git tag --merged=mergetest-2 foo
+test_expect_success '--merged can be used in non-list mode' '
+       cat >expect <<-\EOF &&
+       mergetest-1
+       mergetest-2
+       EOF
+       git tag --merged=mergetest-2 "mergetest*" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success '--merged is incompatible with --no-merged' '
+       test_must_fail git tag --merged HEAD --no-merged HEAD
 '
 
 test_expect_success '--merged shows merged tags' '
@@ -1765,6 +1936,11 @@ test_expect_success '--no-merged show unmerged tags' '
        test_cmp expect actual
 '
 
+test_expect_success '--no-merged can be used in non-list mode' '
+       git tag --no-merged=mergetest-2 mergetest-* >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'ambiguous branch/tags not marked' '
        git tag ambiguous &&
        git branch ambiguous &&
@@ -1773,4 +1949,47 @@ test_expect_success 'ambiguous branch/tags not marked' '
        test_cmp expect actual
 '
 
+test_expect_success '--contains combined with --no-contains' '
+       (
+               git init no-contains &&
+               cd no-contains &&
+               test_commit v0.1 &&
+               test_commit v0.2 &&
+               test_commit v0.3 &&
+               test_commit v0.4 &&
+               test_commit v0.5 &&
+               cat >expected <<-\EOF &&
+               v0.2
+               v0.3
+               v0.4
+               EOF
+               git tag --contains v0.2 --no-contains v0.5 >actual &&
+               test_cmp expected actual
+       )
+'
+
+# As the docs say, list tags which contain a specified *commit*. We
+# don't recurse down to tags for trees or blobs pointed to by *those*
+# commits.
+test_expect_success 'Does --[no-]contains stop at commits? Yes!' '
+       cd no-contains &&
+       blob=$(git rev-parse v0.3:v0.3.t) &&
+       tree=$(git rev-parse v0.3^{tree}) &&
+       git tag tag-blob $blob &&
+       git tag tag-tree $tree &&
+       git tag --contains v0.3 >actual &&
+       cat >expected <<-\EOF &&
+       v0.3
+       v0.4
+       v0.5
+       EOF
+       test_cmp expected actual &&
+       git tag --no-contains v0.3 >actual &&
+       cat >expected <<-\EOF &&
+       v0.1
+       v0.2
+       EOF
+       test_cmp expected actual
+'
+
 test_done
index c8dc665f2fdd0f0452779e6f8576395f8b3bdf11..4f3794d415e95b0b65ca29b829ce278b45ea5dbd 100755 (executable)
@@ -360,27 +360,37 @@ test_pager_choices                       'git aliasedlog'
 test_default_pager        expect_success 'git -p aliasedlog'
 test_PAGER_overrides      expect_success 'git -p aliasedlog'
 test_core_pager_overrides expect_success 'git -p aliasedlog'
-test_core_pager_subdir    expect_failure 'git -p aliasedlog'
+test_core_pager_subdir    expect_success 'git -p aliasedlog'
 test_GIT_PAGER_overrides  expect_success 'git -p aliasedlog'
 
 test_default_pager        expect_success 'git -p true'
 test_PAGER_overrides      expect_success 'git -p true'
 test_core_pager_overrides expect_success 'git -p true'
-test_core_pager_subdir    expect_failure 'git -p true'
+test_core_pager_subdir    expect_success 'git -p true'
 test_GIT_PAGER_overrides  expect_success 'git -p true'
 
 test_default_pager        expect_success test_must_fail 'git -p request-pull'
 test_PAGER_overrides      expect_success test_must_fail 'git -p request-pull'
 test_core_pager_overrides expect_success test_must_fail 'git -p request-pull'
-test_core_pager_subdir    expect_failure test_must_fail 'git -p request-pull'
+test_core_pager_subdir    expect_success test_must_fail 'git -p request-pull'
 test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p request-pull'
 
 test_default_pager        expect_success test_must_fail 'git -p'
 test_PAGER_overrides      expect_success test_must_fail 'git -p'
 test_local_config_ignored expect_failure test_must_fail 'git -p'
-test_no_local_config_subdir expect_success test_must_fail 'git -p'
 test_GIT_PAGER_overrides  expect_success test_must_fail 'git -p'
 
+test_expect_success TTY 'core.pager in repo config works and retains cwd' '
+       sane_unset GIT_PAGER &&
+       test_config core.pager "cat >cwd-retained" &&
+       (
+               cd sub &&
+               rm -f cwd-retained &&
+               test_terminal git -p rev-parse HEAD &&
+               test_path_is_file cwd-retained
+       )
+'
+
 test_doesnt_paginate      expect_failure test_must_fail 'git -p nonsense'
 
 test_pager_choices                       'git shortlog'
index d62ccbb98e6148fa2677bbb763c17b3321168635..b4b49eeb08313df8d260a17414a19e7ac2fdc901 100755 (executable)
@@ -125,18 +125,16 @@ test_expect_success GPG 'verify multiple tags' '
        test_cmp expect.stderr actual.stderr
 '
 
-test_expect_success 'verifying tag with --format' '
-       cat >expect <<-\EOF
+test_expect_success GPG 'verifying tag with --format' '
+       cat >expect <<-\EOF &&
        tagname : fourth-signed
-       EOF &&
+       EOF
        git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual &&
        test_cmp expect actual
 '
 
-test_expect_success 'verifying a forged tag with --format fail and format accordingly' '
-       cat >expect <<-\EOF
-       tagname : 7th forged-signed
-       EOF &&
+test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
+       >expect &&
        test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
        test_cmp expect actual-forged
 '
index c09ce0d4c1dbd8bc49595be4e3928032fbdc3c8c..c2706fe4723778b68b9673fd85267ea475f791f2 100755 (executable)
@@ -273,6 +273,20 @@ test_expect_success 'submodule add with ./, /.. and // in path' '
        test_cmp empty untracked
 '
 
+test_expect_success 'submodule add with \\ in path' '
+       test_when_finished "rm -rf parent sub\\with\\backslash" &&
+
+       # Initialize a repo with a backslash in its name
+       git init sub\\with\\backslash &&
+       touch sub\\with\\backslash/empty.file &&
+       git -C sub\\with\\backslash add empty.file &&
+       git -C sub\\with\\backslash commit -m "Added empty.file" &&
+
+       # Add that repository as a submodule
+       git init parent &&
+       git -C parent submodule add ../sub\\with\\backslash
+'
+
 test_expect_success 'submodule add in subdirectory' '
        echo "refs/heads/master" >expect &&
        >empty &&
@@ -1130,5 +1144,141 @@ test_expect_success 'submodule helper list is not confused by common prefixes' '
        test_cmp expect actual
 '
 
+test_expect_success 'setup superproject with submodules' '
+       git init sub1 &&
+       test_commit -C sub1 test &&
+       test_commit -C sub1 test2 &&
+       git init multisuper &&
+       git -C multisuper submodule add ../sub1 sub0 &&
+       git -C multisuper submodule add ../sub1 sub1 &&
+       git -C multisuper submodule add ../sub1 sub2 &&
+       git -C multisuper submodule add ../sub1 sub3 &&
+       git -C multisuper commit -m "add some submodules"
+'
+
+cat >expect <<-EOF
+-sub0
+ sub1 (test2)
+ sub2 (test2)
+ sub3 (test2)
+EOF
+
+test_expect_success 'submodule update --init with a specification' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       pwd=$(pwd) &&
+       git clone file://"$pwd"/multisuper multisuper_clone &&
+       git -C multisuper_clone submodule update --init . ":(exclude)sub0" &&
+       git -C multisuper_clone submodule status |cut -c 1,43- >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'submodule update --init with submodule.active set' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       pwd=$(pwd) &&
+       git clone file://"$pwd"/multisuper multisuper_clone &&
+       git -C multisuper_clone config submodule.active "." &&
+       git -C multisuper_clone config --add submodule.active ":(exclude)sub0" &&
+       git -C multisuper_clone submodule update --init &&
+       git -C multisuper_clone submodule status |cut -c 1,43- >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'submodule update and setting submodule.<name>.active' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       pwd=$(pwd) &&
+       git clone file://"$pwd"/multisuper multisuper_clone &&
+       git -C multisuper_clone config --bool submodule.sub0.active "true" &&
+       git -C multisuper_clone config --bool submodule.sub1.active "false" &&
+       git -C multisuper_clone config --bool submodule.sub2.active "true" &&
+
+       cat >expect <<-\EOF &&
+        sub0 (test2)
+       -sub1
+        sub2 (test2)
+       -sub3
+       EOF
+       git -C multisuper_clone submodule update &&
+       git -C multisuper_clone submodule status |cut -c 1,43- >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone --recurse-submodules with a pathspec works' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       cat >expected <<-\EOF &&
+        sub0 (test2)
+       -sub1
+       -sub2
+       -sub3
+       EOF
+
+       git clone --recurse-submodules="sub0" multisuper multisuper_clone &&
+       git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+       test_cmp actual expected
+'
+
+test_expect_success 'clone with multiple --recurse-submodules options' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       cat >expect <<-\EOF &&
+       -sub0
+        sub1 (test2)
+       -sub2
+        sub3 (test2)
+       EOF
+
+       git clone --recurse-submodules="." \
+                 --recurse-submodules=":(exclude)sub0" \
+                 --recurse-submodules=":(exclude)sub2" \
+                 multisuper multisuper_clone &&
+       git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'clone and subsequent updates correctly auto-initialize submodules' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       cat <<-\EOF >expect &&
+       -sub0
+        sub1 (test2)
+       -sub2
+        sub3 (test2)
+       EOF
+
+       cat <<-\EOF >expect2 &&
+       -sub0
+        sub1 (test2)
+       -sub2
+        sub3 (test2)
+       -sub4
+        sub5 (test2)
+       EOF
+
+       git clone --recurse-submodules="." \
+                 --recurse-submodules=":(exclude)sub0" \
+                 --recurse-submodules=":(exclude)sub2" \
+                 --recurse-submodules=":(exclude)sub4" \
+                 multisuper multisuper_clone &&
+
+       git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+       test_cmp expect actual &&
+
+       git -C multisuper submodule add ../sub1 sub4 &&
+       git -C multisuper submodule add ../sub1 sub5 &&
+       git -C multisuper commit -m "add more submodules" &&
+       # obtain the new superproject
+       git -C multisuper_clone pull &&
+       git -C multisuper_clone submodule update --init &&
+       git -C multisuper_clone submodule status |cut -c1,43- >actual &&
+       test_cmp expect2 actual
+'
+
+test_expect_success 'init properly sets the config' '
+       test_when_finished "rm -rf multisuper_clone" &&
+       git clone --recurse-submodules="." \
+                 --recurse-submodules=":(exclude)sub0" \
+                 multisuper multisuper_clone &&
+
+       git -C multisuper_clone submodule init -- sub0 sub1 &&
+       git -C multisuper_clone config --get submodule.sub0.active &&
+       test_must_fail git -C multisuper_clone config --get submodule.sub1.active
+'
 
 test_done
index 347857fa7c7cb12a4bc9fd6a2f5373347a02db5c..4ac386d98b8ebdfb4808b9a8119900ee23b5dfa9 100755 (executable)
@@ -442,11 +442,11 @@ test_expect_success 'submodule update - command in .git/config catches failure -
 '
 
 test_expect_success 'submodule update - command run for initial population of submodule' '
-       cat <<-\ EOF >expect
+       cat >expect <<-EOF &&
        Execution of '\''false $submodulesha1'\'' failed in submodule path '\''submodule'\''
-       EOF &&
+       EOF
        rm -rf super/submodule &&
-       test_must_fail git -C super submodule update >../actual &&
+       test_must_fail git -C super submodule update 2>actual &&
        test_cmp expect actual &&
        git -C super submodule update --checkout
 '
diff --git a/t/t7413-submodule-is-active.sh b/t/t7413-submodule-is-active.sh
new file mode 100755 (executable)
index 0000000..9c785b0
--- /dev/null
@@ -0,0 +1,107 @@
+#!/bin/sh
+
+test_description='Test submodule--helper is-active
+
+This test verifies that `git submodue--helper is-active` correclty identifies
+submodules which are "active" and interesting to the user.
+'
+
+. ./test-lib.sh
+
+test_expect_success 'setup' '
+       git init sub &&
+       test_commit -C sub initial &&
+       git init super &&
+       test_commit -C super initial &&
+       git -C super submodule add ../sub sub1 &&
+       git -C super submodule add ../sub sub2 &&
+
+       # Remove submodule.<name>.active entries in order to test in an
+       # environment where only URLs are present in the conifg
+       git -C super config --unset submodule.sub1.active &&
+       git -C super config --unset submodule.sub2.active &&
+
+       git -C super commit -a -m "add 2 submodules at sub{1,2}"
+'
+
+test_expect_success 'is-active works with urls' '
+       git -C super submodule--helper is-active sub1 &&
+       git -C super submodule--helper is-active sub2 &&
+
+       git -C super config --unset submodule.sub1.URL &&
+       test_must_fail git -C super submodule--helper is-active sub1 &&
+       git -C super config submodule.sub1.URL ../sub &&
+       git -C super submodule--helper is-active sub1
+'
+
+test_expect_success 'is-active works with submodule.<name>.active config' '
+       test_when_finished "git -C super config --unset submodule.sub1.active" &&
+       test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
+
+       git -C super config --bool submodule.sub1.active "false" &&
+       test_must_fail git -C super submodule--helper is-active sub1 &&
+
+       git -C super config --bool submodule.sub1.active "true" &&
+       git -C super config --unset submodule.sub1.URL &&
+       git -C super submodule--helper is-active sub1
+'
+
+test_expect_success 'is-active works with basic submodule.active config' '
+       test_when_finished "git -C super config submodule.sub1.URL ../sub" &&
+       test_when_finished "git -C super config --unset-all submodule.active" &&
+
+       git -C super config --add submodule.active "." &&
+       git -C super config --unset submodule.sub1.URL &&
+
+       git -C super submodule--helper is-active sub1 &&
+       git -C super submodule--helper is-active sub2
+'
+
+test_expect_success 'is-active correctly works with paths that are not submodules' '
+       test_when_finished "git -C super config --unset-all submodule.active" &&
+
+       test_must_fail git -C super submodule--helper is-active not-a-submodule &&
+
+       git -C super config --add submodule.active "." &&
+       test_must_fail git -C super submodule--helper is-active not-a-submodule
+'
+
+test_expect_success 'is-active works with exclusions in submodule.active config' '
+       test_when_finished "git -C super config --unset-all submodule.active" &&
+
+       git -C super config --add submodule.active "." &&
+       git -C super config --add submodule.active ":(exclude)sub1" &&
+
+       test_must_fail git -C super submodule--helper is-active sub1 &&
+       git -C super submodule--helper is-active sub2
+'
+
+test_expect_success 'is-active with submodule.active and submodule.<name>.active' '
+       test_when_finished "git -C super config --unset-all submodule.active" &&
+       test_when_finished "git -C super config --unset submodule.sub1.active" &&
+       test_when_finished "git -C super config --unset submodule.sub2.active" &&
+
+       git -C super config --add submodule.active "sub1" &&
+       git -C super config --bool submodule.sub1.active "false" &&
+       git -C super config --bool submodule.sub2.active "true" &&
+
+       test_must_fail git -C super submodule--helper is-active sub1 &&
+       git -C super submodule--helper is-active sub2
+'
+
+test_expect_success 'is-active, submodule.active and submodule add' '
+       test_when_finished "rm -rf super2" &&
+       git init super2 &&
+       test_commit -C super2 initial &&
+       git -C super2 config --add submodule.active "sub*" &&
+
+       # submodule add should only add submodule.<name>.active
+       # to the config if not matched by the pathspec
+       git -C super2 submodule add ../sub sub1 &&
+       test_must_fail git -C super2 config --get submodule.sub1.active &&
+
+       git -C super2 submodule add ../sub mod &&
+       git -C super2 config --get submodule.mod.active
+'
+
+test_done
index 8728db61d38905d9e3e699cb9f450716e4236017..88d4cda2992496d8ff5643caaaccf53e90dedc3a 100755 (executable)
@@ -220,4 +220,21 @@ test_expect_success "hook doesn't edit commit message (editor)" '
 
 '
 
+# set up fake editor to replace `pick` by `reword`
+cat > reword-editor <<'EOF'
+#!/bin/sh
+mv "$1" "$1".bup &&
+sed 's/^pick/reword/' <"$1".bup >"$1"
+EOF
+chmod +x reword-editor
+REWORD_EDITOR="$(pwd)/reword-editor"
+export REWORD_EDITOR
+
+test_expect_success 'hook is called for reword during `rebase -i`' '
+
+       GIT_SEQUENCE_EDITOR="\"$REWORD_EDITOR\"" git rebase -i HEAD^ &&
+       commit_msg_is "new message"
+
+'
+
 test_done
index d31b34da83d5ad336f2355acc0d2536b9d30e3ce..055c90736e9c0cfb60fef7db643f30c7573de581 100755 (executable)
@@ -17,6 +17,12 @@ test_create_repo_with_commit () {
        )
 }
 
+sanitize_output () {
+       sed -e "s/$_x40/HASH/" -e "s/$_x40/HASH/" output >output2 &&
+       mv output2 output
+}
+
+
 test_expect_success 'setup' '
        test_create_repo_with_commit sub &&
        echo output > .gitignore &&
@@ -50,6 +56,15 @@ test_expect_success 'status with modified file in submodule (porcelain)' '
        EOF
 '
 
+test_expect_success 'status with modified file in submodule (short)' '
+       (cd sub && git reset --hard) &&
+       echo "changed" >sub/foo &&
+       git status --short >output &&
+       diff output - <<-\EOF
+        m sub
+       EOF
+'
+
 test_expect_success 'status with added file in submodule' '
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        git status >output &&
@@ -64,6 +79,14 @@ test_expect_success 'status with added file in submodule (porcelain)' '
        EOF
 '
 
+test_expect_success 'status with added file in submodule (short)' '
+       (cd sub && git reset --hard && echo >foo && git add foo) &&
+       git status --short >output &&
+       diff output - <<-\EOF
+        m sub
+       EOF
+'
+
 test_expect_success 'status with untracked file in submodule' '
        (cd sub && git reset --hard) &&
        echo "content" >sub/new-file &&
@@ -83,6 +106,13 @@ test_expect_success 'status with untracked file in submodule (porcelain)' '
        EOF
 '
 
+test_expect_success 'status with untracked file in submodule (short)' '
+       git status --short >output &&
+       diff output - <<-\EOF
+        ? sub
+       EOF
+'
+
 test_expect_success 'status with added and untracked file in submodule' '
        (cd sub && git reset --hard && echo >foo && git add foo) &&
        echo "content" >sub/new-file &&
@@ -177,8 +207,24 @@ test_expect_success 'status with added file in modified submodule with .git file
        test_i18ngrep "modified:   sub (new commits, modified content)" output
 '
 
+test_expect_success 'status with a lot of untracked files in the submodule' '
+       (
+               cd sub
+               i=0 &&
+               while test $i -lt 1024
+               do
+                       >some-file-$i
+                       i=$(( $i + 1 ))
+               done
+       ) &&
+       git status --porcelain sub 2>err.actual &&
+       test_must_be_empty err.actual &&
+       rm err.actual
+'
+
 test_expect_success 'rm submodule contents' '
-       rm -rf sub/* sub/.git
+       rm -rf sub &&
+       mkdir sub
 '
 
 test_expect_success 'status clean (empty submodule dir)' '
@@ -271,4 +317,91 @@ test_expect_success 'diff --submodule with merge conflict in .gitmodules' '
        test_cmp diff_submodule_actual diff_submodule_expect
 '
 
+# We'll setup different cases for further testing:
+# sub1 will contain a nested submodule,
+# sub2 will have an untracked file
+# sub3 will have an untracked repository
+test_expect_success 'setup superproject with untracked file in nested submodule' '
+       (
+               cd super &&
+               git clean -dfx &&
+               rm .gitmodules &&
+               git submodule add -f ./sub1 &&
+               git submodule add -f ./sub2 &&
+               git submodule add -f ./sub1 sub3 &&
+               git commit -a -m "messy merge in superproject" &&
+               (
+                       cd sub1 &&
+                       git submodule add ../sub2 &&
+                       git commit -a -m "add sub2 to sub1"
+               ) &&
+               git add sub1 &&
+               git commit -a -m "update sub1 to contain nested sub"
+       ) &&
+       echo content >super/sub1/sub2/file &&
+       echo content >super/sub2/file &&
+       git -C super/sub3 clone ../../sub2 untracked_repository
+'
+
+test_expect_success 'status with untracked file in nested submodule (porcelain)' '
+       git -C super status --porcelain >output &&
+       diff output - <<-\EOF
+        M sub1
+        M sub2
+        M sub3
+       EOF
+'
+
+test_expect_success 'status with untracked file in nested submodule (porcelain=2)' '
+       git -C super status --porcelain=2 >output &&
+       sanitize_output output &&
+       diff output - <<-\EOF
+       1 .M S..U 160000 160000 160000 HASH HASH sub1
+       1 .M S..U 160000 160000 160000 HASH HASH sub2
+       1 .M S..U 160000 160000 160000 HASH HASH sub3
+       EOF
+'
+
+test_expect_success 'status with untracked file in nested submodule (short)' '
+       git -C super status --short >output &&
+       diff output - <<-\EOF
+        ? sub1
+        ? sub2
+        ? sub3
+       EOF
+'
+
+test_expect_success 'setup superproject with modified file in nested submodule' '
+       git -C super/sub1/sub2 add file &&
+       git -C super/sub2 add file
+'
+
+test_expect_success 'status with added file in nested submodule (porcelain)' '
+       git -C super status --porcelain >output &&
+       diff output - <<-\EOF
+        M sub1
+        M sub2
+        M sub3
+       EOF
+'
+
+test_expect_success 'status with added file in nested submodule (porcelain=2)' '
+       git -C super status --porcelain=2 >output &&
+       sanitize_output output &&
+       diff output - <<-\EOF
+       1 .M S.M. 160000 160000 160000 HASH HASH sub1
+       1 .M S.M. 160000 160000 160000 HASH HASH sub2
+       1 .M S..U 160000 160000 160000 HASH HASH sub3
+       EOF
+'
+
+test_expect_success 'status with added file in nested submodule (short)' '
+       git -C super status --short >output &&
+       diff output - <<-\EOF
+        m sub1
+        m sub2
+        ? sub3
+       EOF
+'
+
 test_done
index 25241f40967ef1fb547771e64c69927de2f101ce..7f09867478c408cddee6b60639bfdf7c845421d8 100755 (executable)
@@ -393,6 +393,25 @@ test_expect_success 'setup change in subdirectory' '
        git commit -m "modified both"
 '
 
+test_expect_success 'difftool -d with growing paths' '
+       a=aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa &&
+       git init growing &&
+       (
+               cd growing &&
+               echo "test -f \"\$2/b\"" | write_script .git/test-for-b.sh &&
+               one=$(printf 1 | git hash-object -w --stdin) &&
+               two=$(printf 2 | git hash-object -w --stdin) &&
+               git update-index --add \
+                       --cacheinfo 100644,$one,$a --cacheinfo 100644,$two,b &&
+               tree1=$(git write-tree) &&
+               git update-index --add \
+                       --cacheinfo 100644,$two,$a --cacheinfo 100644,$one,b &&
+               tree2=$(git write-tree) &&
+               git checkout -- $a &&
+               git difftool -d --extcmd .git/test-for-b.sh $tree1 $tree2
+       )
+'
+
 run_dir_diff_test () {
        test_expect_success "$1 --no-symlinks" "
                symlinks=--no-symlinks &&
@@ -428,7 +447,7 @@ run_dir_diff_test 'difftool --dir-diff branch from subdirectory' '
                git difftool --dir-diff $symlinks --extcmd ls branch >output &&
                # "sub" must only exist in "right"
                # "file" and "file2" must be listed in both "left" and "right"
-               grep sub output > sub-output &&
+               grep sub output >sub-output &&
                test_line_count = 1 sub-output &&
                grep file"$" output >file-output &&
                test_line_count = 2 file-output &&
@@ -591,6 +610,7 @@ test_expect_success 'difftool --no-symlinks detects conflict ' '
 '
 
 test_expect_success 'difftool properly honors gitlink and core.worktree' '
+       test_when_finished rm -rf submod/ule &&
        git submodule add ./. submod/ule &&
        test_config -C submod/ule diff.tool checktrees &&
        test_config -C submod/ule difftool.checktrees.cmd '\''
@@ -600,11 +620,13 @@ test_expect_success 'difftool properly honors gitlink and core.worktree' '
                cd submod/ule &&
                echo good >expect &&
                git difftool --tool=checktrees --dir-diff HEAD~ >actual &&
-               test_cmp expect actual
+               test_cmp expect actual &&
+               rm -f expect actual
        )
 '
 
 test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' '
+       test_when_finished git reset --hard &&
        git init dirlinks &&
        (
                cd dirlinks &&
@@ -623,4 +645,64 @@ test_expect_success SYMLINKS 'difftool --dir-diff symlinked directories' '
        )
 '
 
+test_expect_success SYMLINKS 'difftool --dir-diff handles modified symlinks' '
+       test_when_finished git reset --hard &&
+       touch b &&
+       ln -s b c &&
+       git add b c &&
+       test_tick &&
+       git commit -m initial &&
+       touch d &&
+       rm c &&
+       ln -s d c &&
+       cat >expect <<-EOF &&
+               b
+               c
+
+               c
+       EOF
+       git difftool --symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual &&
+
+       git difftool --no-symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual &&
+
+       # The left side contains symlink "c" that points to "b"
+       test_config difftool.cat.cmd "cat \$LOCAL/c" &&
+       printf "%s\n" b >expect &&
+
+       git difftool --symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       git difftool --symlinks --no-symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       # The right side contains symlink "c" that points to "d"
+       test_config difftool.cat.cmd "cat \$REMOTE/c" &&
+       printf "%s\n" d >expect &&
+
+       git difftool --symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       git difftool --no-symlinks --dir-diff --tool cat >actual &&
+       test_cmp expect actual &&
+
+       # Deleted symlinks
+       rm -f c &&
+       cat >expect <<-EOF &&
+               b
+               c
+
+       EOF
+       git difftool --symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual &&
+
+       git difftool --no-symlinks --dir-diff --extcmd ls >output &&
+       grep -v ^/ output >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 67247a01d6dd2bd22970cd05f7b7088e9261186d..5b6eb3a65e26263db526c2e891ae01a4578bf48e 100755 (executable)
@@ -227,6 +227,81 @@ test_expect_success 'grep history with moved submoules' '
        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 &&
+       git -C sub add file &&
+       git -C sub commit -m "add file" &&
+
+       git init parent &&
+       echo "foobar" >parent/file &&
+       git -C parent add file &&
+       mkdir parent/src &&
+       echo "foobar" >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
+       EOF
+       git -C parent grep --recurse-submodules -e "foobar" >actual &&
+       test_cmp expect actual &&
+
+       # Relative path to top
+       cat >expect <<-\EOF &&
+       ../file:foobar
+       file2:foobar
+       ../sub/file:foobar
+       EOF
+       git -C parent/src grep --recurse-submodules -e "foobar" -- .. >actual &&
+       test_cmp expect actual &&
+
+       # Relative path to submodule
+       cat >expect <<-\EOF &&
+       ../sub/file:foobar
+       EOF
+       git -C parent/src grep --recurse-submodules -e "foobar" -- ../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 &&
+       git -C sub add file &&
+       git -C sub commit -m "add file" &&
+
+       git init parent &&
+       mkdir parent/src &&
+       echo "foobar" >parent/src/file &&
+       git -C parent add src/file &&
+       git -C parent submodule add ../sub src/sub &&
+       git -C parent submodule add ../sub sub &&
+       git -C parent commit -m "add files and submodules" &&
+
+       # Verify grep from root works
+       cat >expect <<-\EOF &&
+       src/file:foobar
+       src/sub/file:foobar
+       sub/file:foobar
+       EOF
+       git -C parent grep --recurse-submodules -e "foobar" >actual &&
+       test_cmp expect actual &&
+
+       # Verify grep from a subdir works
+       cat >expect <<-\EOF &&
+       file:foobar
+       sub/file:foobar
+       EOF
+       git -C parent/src grep --recurse-submodules -e "foobar" >actual &&
+       test_cmp expect actual
+'
+
 test_incompatible_with_recurse_submodules ()
 {
        test_expect_success "--recurse-submodules and $1 are incompatible" "
index e37239e657964e948e51e5ac186f5fdf4fab6003..3457d5db64afc09f63e8c7de4c51b932b012c7ee 100755 (executable)
@@ -139,6 +139,22 @@ test_expect_success 'submit with master branch name from argv' '
        )
 '
 
+test_expect_success 'allow submit from branch with same revision but different name' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               test_commit "file8" &&
+               git checkout -b branch1 &&
+               git checkout -b branch2 &&
+               git config git-p4.skipSubmitEdit true &&
+               git config git-p4.allowSubmit "branch1" &&
+               test_must_fail git p4 submit &&
+               git checkout branch1 &&
+               git p4 submit
+       )
+'
+
 #
 # Basic submit tests, the five handled cases
 #
index d711bef2402cb7abd69c0bbcec49f7faba2530dd..5ed28135bea13a6c4082413044d8dcb205449c38 100755 (executable)
@@ -400,6 +400,22 @@ test_expect_success '__gitdir - remote as argument' '
        test_cmp expected "$actual"
 '
 
+test_expect_success '__gitcomp_direct - puts everything into COMPREPLY as-is' '
+       sed -e "s/Z$//g" >expected <<-EOF &&
+       with-trailing-space Z
+       without-trailing-spaceZ
+       --option Z
+       --option=Z
+       $invalid_variable_name Z
+       EOF
+       (
+               cur=should_be_ignored &&
+               __gitcomp_direct "$(cat expected)" &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
 test_expect_success '__gitcomp - trailing space - options' '
        test_gitcomp "--re" "--dry-run --reuse-message= --reedit-message=
                --reset-author" <<-EOF
@@ -555,6 +571,9 @@ test_expect_success '__git_refs - full refs' '
        cat >expected <<-EOF &&
        refs/heads/master
        refs/heads/matching-branch
+       refs/remotes/other/branch-in-other
+       refs/remotes/other/master-in-other
+       refs/tags/matching-tag
        EOF
        (
                cur=refs/heads/ &&
@@ -620,6 +639,7 @@ test_expect_success '__git_refs - configured remote' '
 
 test_expect_success '__git_refs - configured remote - full refs' '
        cat >expected <<-EOF &&
+       HEAD
        refs/heads/branch-in-other
        refs/heads/master-in-other
        refs/tags/tag-in-other
@@ -648,6 +668,7 @@ test_expect_success '__git_refs - configured remote - repo given on the command
 
 test_expect_success '__git_refs - configured remote - full refs - repo given on the command line' '
        cat >expected <<-EOF &&
+       HEAD
        refs/heads/branch-in-other
        refs/heads/master-in-other
        refs/tags/tag-in-other
@@ -692,6 +713,7 @@ test_expect_success '__git_refs - URL remote' '
 
 test_expect_success '__git_refs - URL remote - full refs' '
        cat >expected <<-EOF &&
+       HEAD
        refs/heads/branch-in-other
        refs/heads/master-in-other
        refs/tags/tag-in-other
@@ -775,6 +797,371 @@ test_expect_success '__git_refs - unique remote branches for git checkout DWIMer
        test_cmp expected "$actual"
 '
 
+test_expect_success '__git_refs - after --opt=' '
+       cat >expected <<-EOF &&
+       HEAD
+       master
+       matching-branch
+       other/branch-in-other
+       other/master-in-other
+       matching-tag
+       EOF
+       (
+               cur="--opt=" &&
+               __git_refs "" "" "" "" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - after --opt= - full refs' '
+       cat >expected <<-EOF &&
+       refs/heads/master
+       refs/heads/matching-branch
+       refs/remotes/other/branch-in-other
+       refs/remotes/other/master-in-other
+       refs/tags/matching-tag
+       EOF
+       (
+               cur="--opt=refs/" &&
+               __git_refs "" "" "" refs/ >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git refs - exluding refs' '
+       cat >expected <<-EOF &&
+       ^HEAD
+       ^master
+       ^matching-branch
+       ^other/branch-in-other
+       ^other/master-in-other
+       ^matching-tag
+       EOF
+       (
+               cur=^ &&
+               __git_refs >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git refs - exluding full refs' '
+       cat >expected <<-EOF &&
+       ^refs/heads/master
+       ^refs/heads/matching-branch
+       ^refs/remotes/other/branch-in-other
+       ^refs/remotes/other/master-in-other
+       ^refs/tags/matching-tag
+       EOF
+       (
+               cur=^refs/ &&
+               __git_refs >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'setup for filtering matching refs' '
+       git branch matching/branch &&
+       git tag matching/tag &&
+       git -C otherrepo branch matching/branch-in-other &&
+       git fetch --no-tags other &&
+       rm -f .git/FETCH_HEAD
+'
+
+test_expect_success '__git_refs - dont filter refs unless told so' '
+       cat >expected <<-EOF &&
+       HEAD
+       master
+       matching-branch
+       matching/branch
+       other/branch-in-other
+       other/master-in-other
+       other/matching/branch-in-other
+       matching-tag
+       matching/tag
+       EOF
+       (
+               cur=master &&
+               __git_refs >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - only matching refs' '
+       cat >expected <<-EOF &&
+       matching-branch
+       matching/branch
+       matching-tag
+       matching/tag
+       EOF
+       (
+               cur=mat &&
+               __git_refs "" "" "" "$cur" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - only matching refs - full refs' '
+       cat >expected <<-EOF &&
+       refs/heads/matching-branch
+       refs/heads/matching/branch
+       EOF
+       (
+               cur=refs/heads/mat &&
+               __git_refs "" "" "" "$cur" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - only matching refs - remote on local file system' '
+       cat >expected <<-EOF &&
+       master-in-other
+       matching/branch-in-other
+       EOF
+       (
+               cur=ma &&
+               __git_refs otherrepo "" "" "$cur" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - only matching refs - configured remote' '
+       cat >expected <<-EOF &&
+       master-in-other
+       matching/branch-in-other
+       EOF
+       (
+               cur=ma &&
+               __git_refs other "" "" "$cur" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - only matching refs - remote - full refs' '
+       cat >expected <<-EOF &&
+       refs/heads/master-in-other
+       refs/heads/matching/branch-in-other
+       EOF
+       (
+               cur=refs/heads/ma &&
+               __git_refs other "" "" "$cur" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - only matching refs - checkout DWIMery' '
+       cat >expected <<-EOF &&
+       matching-branch
+       matching/branch
+       matching-tag
+       matching/tag
+       matching/branch-in-other
+       EOF
+       for remote_ref in refs/remotes/other/ambiguous \
+               refs/remotes/remote/ambiguous \
+               refs/remotes/remote/branch-in-remote
+       do
+               git update-ref $remote_ref master &&
+               test_when_finished "git update-ref -d $remote_ref"
+       done &&
+       (
+               cur=mat &&
+               __git_refs "" 1 "" "$cur" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'teardown after filtering matching refs' '
+       git branch -d matching/branch &&
+       git tag -d matching/tag &&
+       git update-ref -d refs/remotes/other/matching/branch-in-other &&
+       git -C otherrepo branch -D matching/branch-in-other
+'
+
+test_expect_success '__git_refs - for-each-ref format specifiers in prefix' '
+       cat >expected <<-EOF &&
+       evil-%%-%42-%(refname)..master
+       EOF
+       (
+               cur="evil-%%-%42-%(refname)..mas" &&
+               __git_refs "" "" "evil-%%-%42-%(refname).." mas >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_complete_refs - simple' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       HEAD Z
+       master Z
+       matching-branch Z
+       other/branch-in-other Z
+       other/master-in-other Z
+       matching-tag Z
+       EOF
+       (
+               cur= &&
+               __git_complete_refs &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_refs - matching' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       matching-branch Z
+       matching-tag Z
+       EOF
+       (
+               cur=mat &&
+               __git_complete_refs &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_refs - remote' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       HEAD Z
+       branch-in-other Z
+       master-in-other Z
+       EOF
+       (
+               cur=
+               __git_complete_refs --remote=other &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_refs - track' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       HEAD Z
+       master Z
+       matching-branch Z
+       other/branch-in-other Z
+       other/master-in-other Z
+       matching-tag Z
+       branch-in-other Z
+       master-in-other Z
+       EOF
+       (
+               cur=
+               __git_complete_refs --track &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_refs - current word' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       matching-branch Z
+       matching-tag Z
+       EOF
+       (
+               cur="--option=mat" &&
+               __git_complete_refs --cur="${cur#*=}" &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_refs - prefix' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       v1.0..matching-branch Z
+       v1.0..matching-tag Z
+       EOF
+       (
+               cur=v1.0..mat &&
+               __git_complete_refs --pfx=v1.0.. --cur=mat &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_refs - suffix' '
+       cat >expected <<-EOF &&
+       HEAD.
+       master.
+       matching-branch.
+       other/branch-in-other.
+       other/master-in-other.
+       matching-tag.
+       EOF
+       (
+               cur= &&
+               __git_complete_refs --sfx=. &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_fetch_refspecs - simple' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       HEAD:HEAD Z
+       branch-in-other:branch-in-other Z
+       master-in-other:master-in-other Z
+       EOF
+       (
+               cur= &&
+               __git_complete_fetch_refspecs other &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_fetch_refspecs - matching' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       branch-in-other:branch-in-other Z
+       EOF
+       (
+               cur=br &&
+               __git_complete_fetch_refspecs other "" br &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_fetch_refspecs - prefix' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       +HEAD:HEAD Z
+       +branch-in-other:branch-in-other Z
+       +master-in-other:master-in-other Z
+       EOF
+       (
+               cur="+" &&
+               __git_complete_fetch_refspecs other "+" ""  &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_fetch_refspecs - fully qualified' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       refs/heads/branch-in-other:refs/heads/branch-in-other Z
+       refs/heads/master-in-other:refs/heads/master-in-other Z
+       refs/tags/tag-in-other:refs/tags/tag-in-other Z
+       EOF
+       (
+               cur=refs/ &&
+               __git_complete_fetch_refspecs other "" refs/ &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
+test_expect_success '__git_complete_fetch_refspecs - fully qualified & prefix' '
+       sed -e "s/Z$//" >expected <<-EOF &&
+       +refs/heads/branch-in-other:refs/heads/branch-in-other Z
+       +refs/heads/master-in-other:refs/heads/master-in-other Z
+       +refs/tags/tag-in-other:refs/tags/tag-in-other Z
+       EOF
+       (
+               cur=+refs/ &&
+               __git_complete_fetch_refspecs other + refs/ &&
+               print_comp
+       ) &&
+       test_cmp expected out
+'
+
 test_expect_success 'teardown after ref completion' '
        git branch -d matching-branch &&
        git tag -d matching-tag &&
index bd357704cce987afa79ec8fce038aa05f8c0a762..5ee124332a713b1dec984090401b08efb2b9cfe9 100644 (file)
@@ -136,17 +136,12 @@ test_tick () {
        export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
-# Stop execution and start a shell. This is useful for debugging tests and
-# only makes sense together with "-v".
+# Stop execution and start a shell. This is useful for debugging tests.
 #
 # Be sure to remove all invocations of this command before submitting.
 
 test_pause () {
-       if test "$verbose" = t; then
-               "$SHELL_PATH" <&6 >&3 2>&4
-       else
-               error >&5 "test_pause requires --verbose"
-       fi
+       "$SHELL_PATH" <&6 >&5 2>&7
 }
 
 # Wrap git in gdb. Adding this to a command can make it easier to
@@ -154,7 +149,7 @@ test_pause () {
 #
 # Example: "debug git checkout master".
 debug () {
-        GIT_TEST_GDB=1 "$@"
+        GIT_TEST_GDB=1 "$@" <&6 >&5 2>&7
 }
 
 # Call test_commit with the arguments
index 86d77c16dd3abcedd3fd4937a73b142a6b83b7b6..13b5696822d7cd054ed00cd5b4111ab39d3606cb 100644 (file)
@@ -342,6 +342,7 @@ fi
 
 exec 5>&1
 exec 6<&0
+exec 7>&2
 if test "$verbose_log" = "t"
 then
        exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
@@ -624,9 +625,9 @@ test_run_ () {
                trace=
                # 117 is magic because it is unlikely to match the exit
                # code of other programs
-               test_eval_ "(exit 117) && $1"
-               if test "$?" != 117; then
-                       error "bug in the test script: broken &&-chain: $1"
+               if test "OK-117" != "$(test_eval_ "(exit 117) && $1${LF}${LF}echo OK-\$?" 3>&1)"
+               then
+                       error "bug in the test script: broken &&-chain or run-away HERE-DOC: $1"
                fi
                trace=$trace_tmp
        fi
index dc90a1fb769c078be2fbf77018242b6fc209acf6..36408046eb28d8af3c90614f73ee8112855b4677 100644 (file)
@@ -347,14 +347,11 @@ static int set_helper_option(struct transport *transport,
 static void standard_options(struct transport *t)
 {
        char buf[16];
-       int n;
        int v = t->verbose;
 
        set_helper_option(t, "progress", t->progress ? "true" : "false");
 
-       n = snprintf(buf, sizeof(buf), "%d", v + 1);
-       if (n >= sizeof(buf))
-               die("impossibly large verbosity value");
+       xsnprintf(buf, sizeof(buf), "%d", v + 1);
        set_helper_option(t, "verbosity", buf);
 
        switch (t->family) {
index 417ed7f19f5fcec5fdf9828749e1e40b207a44e7..4d33138a7525310af3f9f73b699360f159516ad1 100644 (file)
@@ -116,8 +116,8 @@ struct git_transport_data {
        struct child_process *conn;
        int fd[2];
        unsigned got_remote_heads : 1;
-       struct sha1_array extra_have;
-       struct sha1_array shallow;
+       struct oid_array extra_have;
+       struct oid_array shallow;
 };
 
 static int set_git_option(struct git_transport_options *opts,
@@ -447,7 +447,7 @@ static int print_one_push_status(struct ref *ref, const char *dest, int count,
 
 static int measure_abbrev(const struct object_id *oid, int sofar)
 {
-       char hex[GIT_SHA1_HEXSZ + 1];
+       char hex[GIT_MAX_HEXSZ + 1];
        int w = find_unique_abbrev_r(hex, oid->hash, DEFAULT_ABBREV);
 
        return (w < sofar) ? sofar : w;
@@ -1023,19 +1023,22 @@ int transport_push(struct transport *transport,
                              TRANSPORT_RECURSE_SUBMODULES_ONLY)) &&
                    !is_bare_repository()) {
                        struct ref *ref = remote_refs;
-                       struct sha1_array commits = SHA1_ARRAY_INIT;
+                       struct oid_array commits = OID_ARRAY_INIT;
 
                        for (; ref; ref = ref->next)
                                if (!is_null_oid(&ref->new_oid))
-                                       sha1_array_append(&commits, ref->new_oid.hash);
+                                       oid_array_append(&commits,
+                                                         &ref->new_oid);
 
                        if (!push_unpushed_submodules(&commits,
-                                                     transport->remote->name,
+                                                     transport->remote,
+                                                     refspec, refspec_nr,
+                                                     transport->push_options,
                                                      pretend)) {
-                               sha1_array_clear(&commits);
+                               oid_array_clear(&commits);
                                die("Failed to push all needed submodules!");
                        }
-                       sha1_array_clear(&commits);
+                       oid_array_clear(&commits);
                }
 
                if (((flags & TRANSPORT_RECURSE_SUBMODULES_CHECK) ||
@@ -1044,19 +1047,20 @@ int transport_push(struct transport *transport,
                      !pretend)) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
-                       struct sha1_array commits = SHA1_ARRAY_INIT;
+                       struct oid_array commits = OID_ARRAY_INIT;
 
                        for (; ref; ref = ref->next)
                                if (!is_null_oid(&ref->new_oid))
-                                       sha1_array_append(&commits, ref->new_oid.hash);
+                                       oid_array_append(&commits,
+                                                         &ref->new_oid);
 
                        if (find_unpushed_submodules(&commits, transport->remote->name,
                                                &needs_pushing)) {
-                               sha1_array_clear(&commits);
+                               oid_array_clear(&commits);
                                die_with_unpushed_submodules(&needs_pushing);
                        }
                        string_list_clear(&needs_pushing, 0);
-                       sha1_array_clear(&commits);
+                       oid_array_clear(&commits);
                }
 
                if (!(flags & TRANSPORT_RECURSE_SUBMODULES_ONLY))
index 3a8ee19fe837aae6939c3a6b902f6b067dcde9e4..aa15111fefc8ca333edeeeb3ac77a8b946e4c6a5 100644 (file)
@@ -10,6 +10,8 @@
 #include "attr.h"
 #include "split-index.h"
 #include "dir.h"
+#include "submodule.h"
+#include "submodule-config.h"
 
 /*
  * Error messages expected by scripts out of plumbing commands such as
@@ -45,6 +47,9 @@ static const char *unpack_plumbing_errors[NB_UNPACK_TREES_ERROR_TYPES] = {
 
        /* ERROR_WOULD_LOSE_ORPHANED_REMOVED */
        "Working tree file '%s' would be removed by sparse checkout update.",
+
+       /* ERROR_WOULD_LOSE_SUBMODULE */
+       "Submodule '%s' cannot checkout new HEAD.",
 };
 
 #define ERRORMSG(o,type) \
@@ -161,6 +166,8 @@ void setup_unpack_trees_porcelain(struct unpack_trees_options *opts,
                _("The following working tree files would be overwritten by sparse checkout update:\n%s");
        msgs[ERROR_WOULD_LOSE_ORPHANED_REMOVED] =
                _("The following working tree files would be removed by sparse checkout update:\n%s");
+       msgs[ERROR_WOULD_LOSE_SUBMODULE] =
+               _("Cannot update submodule:\n%s");
 
        opts->show_all_errors = 1;
        /* rejected paths may not have a static buffer */
@@ -240,12 +247,75 @@ static void display_error_msgs(struct unpack_trees_options *o)
                fprintf(stderr, _("Aborting\n"));
 }
 
+static int check_submodule_move_head(const struct cache_entry *ce,
+                                    const char *old_id,
+                                    const char *new_id,
+                                    struct unpack_trees_options *o)
+{
+       const struct submodule *sub = submodule_from_ce(ce);
+       if (!sub)
+               return 0;
+
+       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))
+                       return o->gently ? -1 :
+                               add_rejected_path(o, ERROR_WOULD_LOSE_SUBMODULE, ce->name);
+               return 0;
+       case SM_UPDATE_NONE:
+               return 0;
+       case SM_UPDATE_REBASE:
+       case SM_UPDATE_MERGE:
+       case SM_UPDATE_COMMAND:
+       default:
+               warning(_("submodule update strategy not supported for submodule '%s'"), ce->name);
+               return -1;
+       }
+}
+
+static void reload_gitmodules_file(struct index_state *index,
+                                  struct checkout *state)
+{
+       int i;
+       for (i = 0; i < index->cache_nr; i++) {
+               struct cache_entry *ce = index->cache[i];
+               if (ce->ce_flags & CE_UPDATE) {
+                       int r = strcmp(ce->name, ".gitmodules");
+                       if (r < 0)
+                               continue;
+                       else if (r == 0) {
+                               submodule_free();
+                               checkout_entry(ce, state, NULL);
+                               gitmodules_config();
+                               git_config(submodule_config, NULL);
+                       } else
+                               break;
+               }
+       }
+}
+
 /*
  * Unlink the last component and schedule the leading directories for
  * removal, such that empty directories get removed.
  */
 static void unlink_entry(const struct cache_entry *ce)
 {
+       const struct submodule *sub = submodule_from_ce(ce);
+       if (sub) {
+               switch (sub->update_strategy.type) {
+               case SM_UPDATE_UNSPECIFIED:
+               case SM_UPDATE_CHECKOUT:
+               case SM_UPDATE_REBASE:
+               case SM_UPDATE_MERGE:
+                       submodule_move_head(ce->name, "HEAD", NULL,
+                                           SUBMODULE_MOVE_HEAD_FORCE);
+                       break;
+               case SM_UPDATE_NONE:
+               case SM_UPDATE_COMMAND:
+                       return; /* Do not touch the submodule. */
+               }
+       }
        if (!check_leading_path(ce->name, ce_namelen(ce)))
                return;
        if (remove_or_warn(ce->ce_mode, ce->name))
@@ -301,6 +371,9 @@ static int check_updates(struct unpack_trees_options *o)
        remove_marked_cache_entries(index);
        remove_scheduled_dirs();
 
+       if (should_update_submodules() && o->update && !o->dry_run)
+               reload_gitmodules_file(index, &state);
+
        for (i = 0; i < index->cache_nr; i++) {
                struct cache_entry *ce = index->cache[i];
 
@@ -531,12 +604,18 @@ static int switch_cache_bottom(struct traverse_info *info)
        return ret;
 }
 
+static inline int are_same_oid(struct name_entry *name_j, struct name_entry *name_k)
+{
+       return name_j->oid && name_k->oid && !oidcmp(name_j->oid, name_k->oid);
+}
+
 static int traverse_trees_recursive(int n, unsigned long dirmask,
                                    unsigned long df_conflicts,
                                    struct name_entry *names,
                                    struct traverse_info *info)
 {
        int i, ret, bottom;
+       int nr_buf = 0;
        struct tree_desc t[MAX_UNPACK_TREES];
        void *buf[MAX_UNPACK_TREES];
        struct traverse_info newinfo;
@@ -553,18 +632,40 @@ static int traverse_trees_recursive(int n, unsigned long dirmask,
        newinfo.pathlen += tree_entry_len(p) + 1;
        newinfo.df_conflicts |= df_conflicts;
 
+       /*
+        * Fetch the tree from the ODB for each peer directory in the
+        * n commits.
+        *
+        * For 2- and 3-way traversals, we try to avoid hitting the
+        * ODB twice for the same OID.  This should yield a nice speed
+        * up in checkouts and merges when the commits are similar.
+        *
+        * We don't bother doing the full O(n^2) search for larger n,
+        * because wider traversals don't happen that often and we
+        * avoid the search setup.
+        *
+        * When 2 peer OIDs are the same, we just copy the tree
+        * descriptor data.  This implicitly borrows the buffer
+        * data from the earlier cell.
+        */
        for (i = 0; i < n; i++, dirmask >>= 1) {
-               const unsigned char *sha1 = NULL;
-               if (dirmask & 1)
-                       sha1 = names[i].oid->hash;
-               buf[i] = fill_tree_descriptor(t+i, sha1);
+               if (i > 0 && are_same_oid(&names[i], &names[i - 1]))
+                       t[i] = t[i - 1];
+               else if (i > 1 && are_same_oid(&names[i], &names[i - 2]))
+                       t[i] = t[i - 2];
+               else {
+                       const unsigned char *sha1 = NULL;
+                       if (dirmask & 1)
+                               sha1 = names[i].oid->hash;
+                       buf[nr_buf++] = fill_tree_descriptor(t+i, sha1);
+               }
        }
 
        bottom = switch_cache_bottom(&newinfo);
        ret = traverse_trees(n, t, &newinfo);
        restore_cache_bottom(&newinfo, bottom);
 
-       for (i = 0; i < n; i++)
+       for (i = 0; i < nr_buf; i++)
                free(buf[i]);
 
        return ret;
@@ -1358,17 +1459,26 @@ static int verify_uptodate_1(const struct cache_entry *ce,
        if (!lstat(ce->name, &st)) {
                int flags = CE_MATCH_IGNORE_VALID|CE_MATCH_IGNORE_SKIP_WORKTREE;
                unsigned changed = ie_match_stat(o->src_index, ce, &st, flags);
+
+               if (submodule_from_ce(ce)) {
+                       int r = check_submodule_move_head(ce,
+                               "HEAD", oid_to_hex(&ce->oid), o);
+                       if (r)
+                               return o->gently ? -1 :
+                                       add_rejected_path(o, error_type, ce->name);
+                       return 0;
+               }
+
                if (!changed)
                        return 0;
                /*
-                * NEEDSWORK: the current default policy is to allow
-                * submodule to be out of sync wrt the superproject
-                * index.  This needs to be tightened later for
-                * submodules that are marked to be automatically
-                * checked out.
+                * Historic default policy was to allow submodule to be out
+                * of sync wrt the superproject index. If the submodule was
+                * not considered interesting above, we don't care here.
                 */
                if (S_ISGITLINK(ce->ce_mode))
                        return 0;
+
                errno = 0;
        }
        if (errno == ENOENT)
@@ -1407,11 +1517,16 @@ static void invalidate_ce_path(const struct cache_entry *ce,
  * Currently, git does not checkout subprojects during a superproject
  * checkout, so it is not going to overwrite anything.
  */
-static int verify_clean_submodule(const struct cache_entry *ce,
+static int verify_clean_submodule(const char *old_sha1,
+                                 const struct cache_entry *ce,
                                  enum unpack_trees_error_types error_type,
                                  struct unpack_trees_options *o)
 {
-       return 0;
+       if (!submodule_from_ce(ce))
+               return 0;
+
+       return check_submodule_move_head(ce, old_sha1,
+                                        oid_to_hex(&ce->oid), o);
 }
 
 static int verify_clean_subdirectory(const struct cache_entry *ce,
@@ -1427,16 +1542,18 @@ static int verify_clean_subdirectory(const struct cache_entry *ce,
        struct dir_struct d;
        char *pathbuf;
        int cnt = 0;
-       unsigned char sha1[20];
 
-       if (S_ISGITLINK(ce->ce_mode) &&
-           resolve_gitlink_ref(ce->name, "HEAD", sha1) == 0) {
-               /* If we are not going to update the submodule, then
+       if (S_ISGITLINK(ce->ce_mode)) {
+               unsigned char sha1[20];
+               int sub_head = resolve_gitlink_ref(ce->name, "HEAD", sha1);
+               /*
+                * If we are not going to update the submodule, then
                 * we don't care.
                 */
-               if (!hashcmp(sha1, ce->oid.hash))
+               if (!sub_head && !hashcmp(sha1, ce->oid.hash))
                        return 0;
-               return verify_clean_submodule(ce, error_type, o);
+               return verify_clean_submodule(sub_head ? NULL : sha1_to_hex(sha1),
+                                             ce, error_type, o);
        }
 
        /*
@@ -1575,9 +1692,15 @@ static int verify_absent_1(const struct cache_entry *ce,
                path = xmemdupz(ce->name, len);
                if (lstat(path, &st))
                        ret = error_errno("cannot stat '%s'", path);
-               else
-                       ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
-                                                &st, error_type, o);
+               else {
+                       if (submodule_from_ce(ce))
+                               ret = check_submodule_move_head(ce,
+                                                               oid_to_hex(&ce->oid),
+                                                               NULL, o);
+                       else
+                               ret = check_ok_to_remove(path, len, DT_UNKNOWN, NULL,
+                                                        &st, error_type, o);
+               }
                free(path);
                return ret;
        } else if (lstat(ce->name, &st)) {
@@ -1585,6 +1708,10 @@ static int verify_absent_1(const struct cache_entry *ce,
                        return error_errno("cannot stat '%s'", ce->name);
                return 0;
        } else {
+               if (submodule_from_ce(ce))
+                       return check_submodule_move_head(ce, oid_to_hex(&ce->oid),
+                                                        NULL, o);
+
                return check_ok_to_remove(ce->name, ce_namelen(ce),
                                          ce_to_dtype(ce), ce, &st,
                                          error_type, o);
@@ -1640,6 +1767,15 @@ static int merged_entry(const struct cache_entry *ce,
                        return -1;
                }
                invalidate_ce_path(merge, o);
+
+               if (submodule_from_ce(ce)) {
+                       int ret = check_submodule_move_head(ce, NULL,
+                                                           oid_to_hex(&ce->oid),
+                                                           o);
+                       if (ret)
+                               return ret;
+               }
+
        } else if (!(old->ce_flags & CE_CONFLICTED)) {
                /*
                 * See if we can re-use the old CE directly?
@@ -1660,6 +1796,14 @@ static int merged_entry(const struct cache_entry *ce,
                        update |= old->ce_flags & (CE_SKIP_WORKTREE | CE_NEW_SKIP_WORKTREE);
                        invalidate_ce_path(old, o);
                }
+
+               if (submodule_from_ce(ce)) {
+                       int ret = check_submodule_move_head(ce, oid_to_hex(&old->oid),
+                                                           oid_to_hex(&ce->oid),
+                                                           o);
+                       if (ret)
+                               return ret;
+               }
        } else {
                /*
                 * Previously unmerged entry left as an existence
index 36a73a6d003b6906dfb402aa930b352f5b368c60..6c48117b845fbf7b983852be302e4472c5e6d651 100644 (file)
@@ -21,6 +21,7 @@ enum unpack_trees_error_types {
        ERROR_SPARSE_NOT_UPTODATE_FILE,
        ERROR_WOULD_LOSE_ORPHANED_OVERWRITTEN,
        ERROR_WOULD_LOSE_ORPHANED_REMOVED,
+       ERROR_WOULD_LOSE_SUBMODULE,
        NB_UNPACK_TREES_ERROR_TYPES
 };
 
index fa7bc67a50a6d52116b0ca994b473668998f1c3f..bae787cf8d7e968e8d5118bf54f17112c0229bf5 100644 (file)
@@ -250,16 +250,19 @@ struct worktree *find_worktree(struct worktree **list,
 {
        struct worktree *wt;
        char *path;
+       char *to_free = NULL;
 
        if ((wt = find_worktree_by_suffix(list, arg)))
                return wt;
 
-       arg = prefix_filename(prefix, strlen(prefix), arg);
+       if (prefix)
+               arg = to_free = prefix_filename(prefix, arg);
        path = real_pathdup(arg, 1);
        for (; *list; list++)
                if (!fspathcmp(path, real_path((*list)->path)))
                        break;
        free(path);
+       free(to_free);
        return *list;
 }
 
index 0542fc75821fdbdd7bfecebec5f7df01e49dcb0c..d83741770949f457b364896b6ff8632c0a700d69 100644 (file)
--- a/wrapper.c
+++ b/wrapper.c
@@ -655,3 +655,16 @@ void sleep_millisec(int millisec)
 {
        poll(NULL, 0, millisec);
 }
+
+int xgethostname(char *buf, size_t len)
+{
+       /*
+        * If the full hostname doesn't fit in buf, POSIX does not
+        * specify whether the buffer will be null-terminated, so to
+        * be safe, do it ourselves.
+        */
+       int ret = gethostname(buf, len);
+       if (!ret)
+               buf[len - 1] = 0;
+       return ret;
+}
index a8d1faf80d73d360eb82dc6b5c588544a0cc812c..03754849626d1bf117e3821fb7cdffcd594f694f 100644 (file)
@@ -407,6 +407,16 @@ static void wt_longstatus_print_change_data(struct wt_status *s,
        strbuf_release(&twobuf);
 }
 
+static char short_submodule_status(struct wt_status_change_data *d) {
+       if (d->new_submodule_commits)
+               return 'M';
+       if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED)
+               return 'm';
+       if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED)
+               return '?';
+       return d->worktree_status;
+}
+
 static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
                                         struct diff_options *options,
                                         void *data)
@@ -431,10 +441,13 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
                }
                if (!d->worktree_status)
                        d->worktree_status = p->status;
-               d->dirty_submodule = p->two->dirty_submodule;
-               if (S_ISGITLINK(p->two->mode))
+               if (S_ISGITLINK(p->two->mode)) {
+                       d->dirty_submodule = p->two->dirty_submodule;
                        d->new_submodule_commits = !!oidcmp(&p->one->oid,
                                                            &p->two->oid);
+                       if (s->status_format == STATUS_FORMAT_SHORT)
+                               d->worktree_status = short_submodule_status(d);
+               }
 
                switch (p->status) {
                case DIFF_STATUS_ADDED:
@@ -1730,12 +1743,14 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
                return;
        branch_name = s->branch;
 
+#define LABEL(string) (s->no_gettext ? (string) : _(string))
+
        if (s->is_initial)
-               color_fprintf(s->fp, header_color, _("Initial commit on "));
+               color_fprintf(s->fp, header_color, LABEL(N_("Initial commit on ")));
 
        if (!strcmp(s->branch, "HEAD")) {
                color_fprintf(s->fp, color(WT_STATUS_NOBRANCH, s), "%s",
-                             _("HEAD (no branch)"));
+                             LABEL(N_("HEAD (no branch)")));
                goto conclude;
        }
 
@@ -1760,8 +1775,6 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
        if (!upstream_is_gone && !num_ours && !num_theirs)
                goto conclude;
 
-#define LABEL(string) (s->no_gettext ? (string) : _(string))
-
        color_fprintf(s->fp, header_color, " [");
        if (upstream_is_gone) {
                color_fprintf(s->fp, header_color, LABEL(N_("gone")));
@@ -1785,34 +1798,24 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
 
 static void wt_shortstatus_print(struct wt_status *s)
 {
-       int i;
+       struct string_list_item *it;
 
        if (s->show_branch)
                wt_shortstatus_print_tracking(s);
 
-       for (i = 0; i < s->change.nr; i++) {
-               struct wt_status_change_data *d;
-               struct string_list_item *it;
+       for_each_string_list_item(it, &s->change) {
+               struct wt_status_change_data *d = it->util;
 
-               it = &(s->change.items[i]);
-               d = it->util;
                if (d->stagemask)
                        wt_shortstatus_unmerged(it, s);
                else
                        wt_shortstatus_status(it, s);
        }
-       for (i = 0; i < s->untracked.nr; i++) {
-               struct string_list_item *it;
-
-               it = &(s->untracked.items[i]);
+       for_each_string_list_item(it, &s->untracked)
                wt_shortstatus_other(it, s, "??");
-       }
-       for (i = 0; i < s->ignored.nr; i++) {
-               struct string_list_item *it;
 
-               it = &(s->ignored.items[i]);
+       for_each_string_list_item(it, &s->ignored)
                wt_shortstatus_other(it, s, "!!");
-       }
 }
 
 static void wt_porcelain_print(struct wt_status *s)
index 54fec77032dc9c5d0f7af4226afc886797a209c4..6018c627b1e40e5c36d0c0c12eff1f262cb439b4 100644 (file)
@@ -80,7 +80,7 @@ struct wt_status {
        int hints;
 
        enum wt_status_format status_format;
-       unsigned char sha1_commit[GIT_SHA1_RAWSZ]; /* when not Initial */
+       unsigned char sha1_commit[GIT_MAX_RAWSZ]; /* when not Initial */
 
        /* These are computed during processing of the individual sections */
        int commitable;