Merge branch 'ss/submodule-shallow-doc'
authorJunio C Hamano <gitster@pobox.com>
Wed, 26 Apr 2017 06:39:07 +0000 (15:39 +0900)
committerJunio C Hamano <gitster@pobox.com>
Wed, 26 Apr 2017 06:39:07 +0000 (15:39 +0900)
Doc update.

* ss/submodule-shallow-doc:
gitmodules: clarify what history depth a shallow clone has

317 files changed:
.gitignore
.mailmap
.travis.yml
Documentation/RelNotes/2.13.0.txt [new file with mode: 0644]
Documentation/SubmittingPatches
Documentation/config.txt
Documentation/diff-format.txt
Documentation/diff-generate-patch.txt
Documentation/diff-options.txt
Documentation/git-apply.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-ls-tree.txt
Documentation/git-merge.txt
Documentation/git-name-rev.txt
Documentation/git-push.txt
Documentation/git-read-tree.txt
Documentation/git-rebase.txt
Documentation/git-receive-pack.txt
Documentation/git-rev-parse.txt
Documentation/git-send-pack.txt
Documentation/git-stash.txt
Documentation/git-status.txt
Documentation/git-submodule.txt
Documentation/git-tag.txt
Documentation/git-update-index.txt
Documentation/git.txt
Documentation/gitattributes.txt
Documentation/githooks.txt
Documentation/gitmodules.txt
Documentation/glossary-content.txt
Documentation/revisions.txt
Documentation/technical/api-gitattributes.txt
Documentation/technical/api-hashmap.txt
Documentation/technical/api-oid-array.txt [new file with mode: 0644]
Documentation/technical/api-parse-options.txt
Documentation/technical/api-sha1-array.txt [deleted file]
Documentation/technical/pack-protocol.txt
GIT-VERSION-GEN
Makefile
RelNotes
abspath.c
apply.c
archive.c
attr.c
attr.h
bisect.c
branch.c
branch.h
builtin.h
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/bundle.c
builtin/cat-file.c
builtin/check-attr.c
builtin/checkout.c
builtin/clean.c
builtin/clone.c
builtin/commit.c
builtin/config.c
builtin/count-objects.c
builtin/describe.c
builtin/diff-tree.c
builtin/diff.c
builtin/difftool.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.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-base.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/prune-packed.c
builtin/prune.c
builtin/pull.c
builtin/push.c
builtin/read-tree.c
builtin/rebase--helper.c [new file with mode: 0644]
builtin/receive-pack.c
builtin/reflog.c
builtin/remote.c
builtin/replace.c
builtin/reset.c
builtin/rev-list.c
builtin/rev-parse.c
builtin/revert.c
builtin/send-pack.c
builtin/shortlog.c
builtin/submodule--helper.c
builtin/symbolic-ref.c
builtin/tag.c
builtin/update-index.c
builtin/update-ref.c
builtin/worktree.c
bulk-checkin.c
cache.h
ci/run-windows-build.sh [new file with mode: 0755]
combine-diff.c
commit.c
commit.h
common-main.c
config.c
connect.c
contrib/completion/git-completion.bash
contrib/completion/git-completion.zsh
contrib/completion/git-prompt.sh
contrib/git-resurrect.sh
convert.c
credential-cache.c
credential-store.c
daemon.c
diff-no-index.c
diff.c
diff.h
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--interactive.sh
git-rebase.sh
git-stash.sh
git-submodule.sh
git-svn.perl
git.c
grep.c
hash.h [new file with mode: 0644]
hashmap.c
hashmap.h
hex.c
http.c
http.h
ident.c
imap-send.c
ll-merge.c
log-tree.c
log-tree.h
mailinfo.c
merge-recursive.c
name-hash.c
notes.c
object.h
oidset.c [new file with mode: 0644]
oidset.h [new file with mode: 0644]
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
pathspec.h
po/de.po
preload-index.c
pretty.c
reachable.c
read-cache.c
ref-filter.c
ref-filter.h
reflog-walk.c
refs.c
refs.h
refs/files-backend.c
refs/refs-internal.h
remote-curl.c
remote.c
remote.h
revision.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
split-index.c
split-index.h
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/t0003-attributes.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/t1300-repo-config.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/t1500-rev-parse.sh
t/t1507-rev-parse-upstream.sh
t/t1514-rev-parse-push.sh
t/t1700-split-index.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/t3203-branch-output.sh
t/t3404-rebase-interactive.sh
t/t3428-rebase-signoff.sh [new file with mode: 0755]
t/t3502-cherry-pick-merge.sh
t/t3600-rm.sh
t/t3701-add-interactive.sh
t/t3903-stash.sh
t/t3904-stash-patch.sh
t/t3905-stash-include-untracked.sh
t/t4060-diff-submodule-option-diff-format.sh
t/t4150-am.sh
t/t4202-log.sh
t/t5400-send-pack.sh
t/t5505-remote.sh
t/t5531-deep-submodule-push.sh
t/t5533-push-cas.sh
t/t5545-push-options.sh
t/t5547-push-quarantine.sh
t/t5601-clone.sh
t/t6007-rev-list-cherry-pick-file.sh
t/t6040-tracking-info.sh
t/t6045-merge-rename-delete.sh [new file with mode: 0755]
t/t6120-describe.sh
t/t6132-pathspec-exclude.sh
t/t6135-pathspec-with-attrs.sh [new file with mode: 0755]
t/t6300-for-each-ref.sh
t/t6302-for-each-ref-filter.sh
t/t6500-gc.sh
t/t7004-tag.sh
t/t7006-pager.sh
t/t7400-submodule-basic.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.sh
transport-helper.c
transport.c
transport.h
unpack-trees.c
unpack-trees.h
urlmatch.c
urlmatch.h
userdiff.c
worktree.c
wrapper.c
ws.c
wt-status.c
wt-status.h
xdiff/xemit.c
index b1020b875fc006c81ed92ffb93e73876487c7c57..833ef3b0b783b8180d0dad1ce336713bddf09b26 100644 (file)
 /git-read-tree
 /git-rebase
 /git-rebase--am
+/git-rebase--helper
 /git-rebase--interactive
 /git-rebase--merge
 /git-receive-pack
index c44cf67a482567fd640223f777f10bc95acd5b85..ab85e0d16d6383b13954220a0b41202bd68d5d73 100644 (file)
--- a/.mailmap
+++ b/.mailmap
@@ -178,6 +178,7 @@ Paolo Bonzini <bonzini@gnu.org> <paolo.bonzini@lu.unisi.ch>
 Pascal Obry <pascal@obry.net> <pascal.obry@gmail.com>
 Pascal Obry <pascal@obry.net> <pascal.obry@wanadoo.fr>
 Pat Notz <patnotz@gmail.com> <pknotz@sandia.gov>
+Patrick Steinhardt <ps@pks.im> <patrick.steinhardt@elego.de>
 Paul Mackerras <paulus@samba.org> <paulus@dorrigo.(none)>
 Paul Mackerras <paulus@samba.org> <paulus@pogo.(none)>
 Peter Baumann <waste.manager@gmx.de> <Peter.B.Baumann@stud.informatik.uni-erlangen.de>
index 591cc57b80aa415be9c20b3c0dc746d792e68071..297b0bca979d818faec64791ca5167dbd7b50e86 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:
@@ -60,6 +71,18 @@ matrix:
         # Use the following command to debug the docker build locally:
         # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
         # root@container:/# /usr/src/git/ci/run-linux32-build.sh
+    - env: Static Analysis
+      os: linux
+      compiler:
+      addons:
+        apt:
+          packages:
+          - coccinelle
+      before_install:
+      script:
+        # "before_script" that builds Git is inherited from base job
+        - make coccicheck
+      after_failure:
     - env: Documentation
       os: linux
       compiler: clang
diff --git a/Documentation/RelNotes/2.13.0.txt b/Documentation/RelNotes/2.13.0.txt
new file mode 100644 (file)
index 0000000..b757fd1
--- /dev/null
@@ -0,0 +1,559 @@
+Git 2.13 Release Notes
+======================
+
+Backward compatibility notes.
+
+ * Use of an empty string as a pathspec element that is used for
+   'everything matches' is still warned and Git asks users to use a
+   more explicit '.' for that instead.  The hope is that existing
+   users will not mind this change, and eventually the warning can be
+   turned into a hard error, upgrading the deprecation into removal of
+   this (mis)feature.  That is not scheduled to happen in the upcoming
+   release (yet).
+
+ * The historical argument order "git merge <msg> HEAD <commit>..."
+   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
+-------------------
+
+UI, Workflows & Features
+
+ * "git describe" and "git name-rev" have been taught to take more
+   than one refname patterns to restrict the set of refs to base their
+   naming output on, and also learned to take negative patterns to
+   name refs not to be used for naming via their "--exclude" option.
+
+ * Deletion of a branch "foo/bar" could remove .git/refs/heads/foo
+   once there no longer is any other branch whose name begins with
+   "foo/", but we didn't do so so far.  Now we do.
+
+ * When "git merge" detects a path that is renamed in one history
+   while the other history deleted (or modified) it, it now reports
+   both paths to help the user understand what is going on in the two
+   histories being merged.
+
+ * The <url> part in "http.<url>.<variable>" configuration variable
+   can now be spelled with '*' that serves as wildcard.
+   E.g. "http.https://*.example.com.proxy" can be used to specify the
+   proxy used for https://a.example.com, https://b.example.com, etc.,
+   i.e. any host in the example.com domain.
+
+ * "git tag" did not leave useful message when adding a new entry to
+   reflog; this was left unnoticed for a long time because refs/tags/*
+   doesn't keep reflog by default.
+
+ * The "negative" pathspec feature was somewhat more cumbersome to use
+   than necessary in that its short-hand used "!" which needed to be
+   escaped from shells, and it required "exclude from what?" specified.
+
+ * The command line options for ssh invocation needs to be tweaked for
+   some implementations of SSH (e.g. PuTTY plink wants "-P <port>"
+   while OpenSSH wants "-p <port>" to specify port to connect to), and
+   the variant was guessed when GIT_SSH environment variable is used
+   to specify it.  The logic to guess now applies to the command
+   specified by the newer GIT_SSH_COMMAND and also core.sshcommand
+   configuration variable, and comes with an escape hatch for users to
+   deal with misdetected cases.
+
+ * The "--git-path", "--git-common-dir", and "--shared-index-path"
+   options of "git rev-parse" did not produce usable output.  They are
+   now updated to show the path to the correct file, relative to where
+   the caller is.
+
+ * "git diff -W" has been taught to handle the case where a new
+   function is added at the end of the file better.
+
+ * "git update-ref -d" and other operations to delete references did
+   not leave any entry in HEAD's reflog when the reference being
+   deleted was the current branch.  This is not a problem in practice
+   because you do not want to delete the branch you are currently on,
+   but caused renaming of the current branch to something else not to
+   be logged in a useful way.
+
+ * "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.
+
+ * 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.
+
+ * "git stash push" takes a pathspec so that the local changes can be
+   stashed away only partially.
+
+ * 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.
+
+ * The code to list branches in "git branch" has been consolidated
+   with the more generic ref-filter API.
+
+ * Resource usage while enumerating refs from alternate object store
+   has been optimized to help receiving end of "push" that hosts a
+   repository with many "forks".
+
+ * The gitattributes machinery is being taught to work better in a
+   multi-threaded environment.
+
+ * "git rebase -i" starts using the recently updated "sequencer" code.
+
+ * Code and design clean-up for the refs API.
+
+ * The preload-index code has been taught not to bother with the index
+   entries that are paths that are not checked out by "sparse checkout".
+
+ * Some warning() messages from "git clean" were updated to show the
+   errno from failed system calls.
+
+ * The "parse_config_key()" API function has been cleaned up.
+
+ * A test that creates a confusing branch whose name is HEAD has been
+   corrected not to do so.
+
+ * The code that parses header fields in the commit object has been
+   updated for (micro)performance and code hygiene.
+
+ * An helper function to make it easier to append the result from
+   real_path() to a strbuf has been added.
+
+ * Reduce authentication round-trip over HTTP when the server supports
+   just a single authentication method.  This also improves the
+   behaviour when Git is misconfigured to enable http.emptyAuth
+   against a server that does not authenticate without a username
+   (i.e. not using Kerberos etc., which makes http.emptyAuth
+   pointless).
+
+ * Windows port wants to use OpenSSL's implementation of SHA-1
+   routines, so let them.
+
+ * 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.
+
+ * Add 32-bit Linux variant to the set of platforms to be tested with
+   Travis CI.
+
+ * "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.
+
+
+Fixes since v2.12
+-----------------
+
+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.
+
+ * 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).
+
+ * "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.
+
+ * "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.
+
+ * 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 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 "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.
+
+ * 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.
+
+ * user.email that consists of only cruft chars should consistently
+   error out, but didn't.
+
+ * "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 leak in a codepath to read from a packed object in (rare) cases
+   has been plugged.
+
+ * 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.
+
+ * 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.
+
+ * 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.
+
+ * There is no need for Python only to give a few messages to the
+   standard error stream, but we somehow did.
+
+ * 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.
+
+ * The command-line parsing of "git log -L" copied internal data
+   structures using incorrect size on ILP32 systems.
+
+ * "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).
+
+ * 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.
+
+ * "git push" had a handful of codepaths that could lead to a deadlock
+   when unexpected error happened, which has been fixed.
+
+ * "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 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 e43d147825cede8fd8e1fcfb12946d4f92636d11..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
 ~~~~~~
@@ -334,6 +396,10 @@ core.trustctime::
        crawlers and some backup systems).
        See linkgit:git-update-index[1]. True by default.
 
+core.splitIndex::
+       If true, the split-index feature of the index will be used.
+       See linkgit:git-update-index[1]. False by default.
+
 core.untrackedCache::
        Determines what to do about the untracked cache feature of the
        index. It will be kept, if this variable is unset or set to
@@ -350,16 +416,19 @@ core.checkStat::
        all fields, including the sub-second part of mtime and ctime.
 
 core.quotePath::
-       The commands that output paths (e.g. 'ls-files',
-       'diff'), when not given the `-z` option, will quote
-       "unusual" characters in the pathname by enclosing the
-       pathname in a double-quote pair and with backslashes the
-       same way strings in C source code are quoted.  If this
-       variable is set to false, the bytes higher than 0x80 are
-       not quoted but output as verbatim.  Note that double
-       quote, backslash and control characters are always
-       quoted without `-z` regardless of the setting of this
-       variable.
+       Commands that output paths (e.g. 'ls-files', 'diff'), will
+       quote "unusual" characters in the pathname by enclosing the
+       pathname in double-quotes and escaping those characters with
+       backslashes in the same way C escapes control characters (e.g.
+       `\t` for TAB, `\n` for LF, `\\` for backslash) or bytes with
+       values larger than 0x80 (e.g. octal `\302\265` for "micro" in
+       UTF-8).  If this variable is set to false, bytes higher than
+       0x80 are not considered "unusual" any more. Double-quotes,
+       backslash and control characters are always escaped regardless
+       of the setting of this variable.  A simple space character is
+       not considered "unusual".  Many commands can output pathnames
+       completely verbatim using the `-z` option. The default value
+       is true.
 
 core.eol::
        Sets the line ending type to use in the working directory for
@@ -1925,7 +1994,10 @@ http.<url>.*::
   must match exactly between the config key and the URL.
 
 . Host/domain name (e.g., `example.com` in `https://example.com/`).
-  This field must match exactly between the config key and the URL.
+  This field must match between the config key and the URL. It is
+  possible to specify a `*` as part of the host name to match all subdomains
+  at this level. `https://*.example.com/` for example would match
+  `https://foo.example.com/`, but not `https://foo.bar.example.com/`.
 
 . Port number (e.g., `8080` in `http://example.com:8080/`).
   This field must match exactly between the config key and the URL.
@@ -1960,6 +2032,17 @@ Environment variable settings always override any matches.  The URLs that are
 matched against are those given directly to Git commands.  This means any URLs
 visited as a result of a redirection do not participate in matching.
 
+ssh.variant::
+       Depending on the value of the environment variables `GIT_SSH` or
+       `GIT_SSH_COMMAND`, or the config setting `core.sshCommand`, Git
+       auto-detects whether to adjust its command-line parameters for use
+       with plink or tortoiseplink, as opposed to the default (OpenSSH).
++
+The config variable `ssh.variant` can be set to override this auto-detection;
+valid values are `ssh`, `plink`, `putty` or `tortoiseplink`. Any other value
+will be treated as normal ssh. This setting can be overridden via the
+environment variable `GIT_SSH_VARIANT`.
+
 i18n.commitEncoding::
        Character encoding the commit messages are stored in; Git itself
        does not care per se, but this information is necessary e.g. when
@@ -2835,6 +2918,31 @@ showbranch.default::
        The default set of branches for linkgit:git-show-branch[1].
        See linkgit:git-show-branch[1].
 
+splitIndex.maxPercentChange::
+       When the split index feature is used, this specifies the
+       percent of entries the split index can contain compared to the
+       total number of entries in both the split index and the shared
+       index before a new shared index is written.
+       The value should be between 0 and 100. If the value is 0 then
+       a new shared index is always written, if it is 100 a new
+       shared index is never written.
+       By default the value is 20, so a new shared index is written
+       if the number of entries in the split index would be greater
+       than 20 percent of the total number of entries.
+       See linkgit:git-update-index[1].
+
+splitIndex.sharedIndexExpire::
+       When the split index feature is used, shared index files that
+       were not modified since the time this variable specifies will
+       be removed when a new shared index file is created. The value
+       "now" expires all entries immediately, and "never" suppresses
+       expiration altogether.
+       The default value is "2.weeks.ago".
+       Note that a shared index file is considered modified (for the
+       purpose of expiration) each time a new split-index file is
+       either created based on it or read from it.
+       See linkgit:git-update-index[1].
+
 status.relativePaths::
        By default, linkgit:git-status[1] shows paths relative to the
        current directory. Setting this variable to `false` shows paths
@@ -2905,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::
@@ -2944,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 cf5262622f6a7f82b3989cbcb4ee1ebd4652b220..706916c94c1d930ef1fe476c7a16c140aeef7420 100644 (file)
@@ -78,9 +78,10 @@ Example:
 :100644 100644 5be4a4...... 000000...... M file.c
 ------------------------------------------------
 
-When `-z` option is not used, TAB, LF, and backslash characters
-in pathnames are represented as `\t`, `\n`, and `\\`,
-respectively.
+Without the `-z` option, pathnames with "unusual" characters are
+quoted as explained for the configuration variable `core.quotePath`
+(see linkgit:git-config[1]).  Using `-z` the filename is output
+verbatim and the line is terminated by a NUL byte.
 
 diff format for merges
 ----------------------
index d2a7ff56e860b92c4dd346a0ea1eb2eb9f7fde79..231105cff48d9c109b2001623c67d51690c595ba 100644 (file)
@@ -53,10 +53,9 @@ The index line includes the SHA-1 checksum before and after the change.
 The <mode> is included if the file mode does not change; otherwise,
 separate lines indicate the old and the new mode.
 
-3.  TAB, LF, double quote and backslash characters in pathnames
-    are represented as `\t`, `\n`, `\"` and `\\`, respectively.
-    If there is need for such substitution then the whole
-    pathname is put in double quotes.
+3.  Pathnames with "unusual" characters are quoted as explained for
+    the configuration variable `core.quotePath` (see
+    linkgit:git-config[1]).
 
 4.  All the `file1` files in the output refer to files before the
     commit, and all the `file2` files refer to files after the commit.
index d91ddbd5fe490527bd3032789d0bf3f2eef93f93..89cc0f48deef7152d59f4d4665ff3179ed8e4315 100644 (file)
@@ -192,10 +192,9 @@ ifndef::git-log[]
        given, do not munge pathnames and use NULs as output field terminators.
 endif::git-log[]
 +
-Without this option, each pathname output will have TAB, LF, double quotes,
-and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`,
-respectively, and the pathname will be enclosed in double quotes if
-any of those replacements occurred.
+Without this option, pathnames with "unusual" characters are quoted as
+explained for the configuration variable `core.quotePath` (see
+linkgit:git-config[1]).
 
 --name-only::
        Show only names of changed files.
index 8ddb207409df99825b561be42234e9c23707287d..631cbd840a08eb29add1e900ac9272992669a75c 100644 (file)
@@ -108,10 +108,9 @@ the information is read from the current index instead.
        When `--numstat` has been given, do not munge pathnames,
        but use a NUL-terminated machine-readable format.
 +
-Without this option, each pathname output will have TAB, LF, double quotes,
-and backslash characters replaced with `\t`, `\n`, `\"`, and `\\`,
-respectively, and the pathname will be enclosed in double quotes if
-any of those replacements occurred.
+Without this option, pathnames with "unusual" characters are quoted as
+explained for the configuration variable `core.quotePath` (see
+linkgit:git-config[1]).
 
 -p<n>::
        Remove <n> leading slashes from traditional diff paths. The
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 28d46cc03b217c156769f6123c7b3e3351eabce4..81bd0a7b7741f175cf7a99e2aa9cbcacf42da78e 100644 (file)
@@ -10,9 +10,10 @@ 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>]
-       [--points-at <object>] [<pattern>...]
+       [--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>]
 'git branch' --unset-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.
@@ -253,6 +266,11 @@ start-point is either a local or remote-tracking branch.
 --points-at <object>::
        Only list branches of the given object.
 
+--format <format>::
+       A string that interpolates `%(fieldname)` from the object
+       pointed at by a ref being shown.  The format is the same as
+       that of linkgit:git-for-each-ref[1].
+
 Examples
 --------
 
@@ -291,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 2763edb7a71d9e0fba3487a2f371733ff682e0a5..ed0f5b94b3f200676aedb312391a2dd8fde7325d 100644 (file)
@@ -117,9 +117,12 @@ OPTIONS
 
 -z::
 --null::
-       When showing `short` or `porcelain` status output, terminate
-       entries in the status output with NUL, instead of LF. If no
-       format is given, implies the `--porcelain` output format.
+       When showing `short` or `porcelain` status output, print the
+       filename verbatim and terminate the entries with NUL, instead of LF.
+       If no format is given, implies the `--porcelain` output format.
+       Without the `-z` option, filenames with "unusual" characters are
+       quoted as explained for the configuration variable `core.quotePath`
+       (see linkgit:git-config[1]).
 
 -F <file>::
 --file=<file>::
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 e4ac448ff565a0951444d0b1b98b2d5f86f01a9f..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
@@ -83,7 +88,20 @@ OPTIONS
 --match <pattern>::
        Only consider tags matching the given `glob(7)` pattern,
        excluding the "refs/tags/" prefix.  This can be used to avoid
-       leaking private tags from the repository.
+       leaking private tags from the repository. If given multiple times, a
+       list of patterns will be accumulated, and tags matching any of the
+       patterns will be considered. Use `--no-match` to clear and reset the
+       list of patterns.
+
+--exclude <pattern>::
+       Do not consider tags matching the given `glob(7)` pattern, excluding
+       the "refs/tags/" prefix. This can be used to narrow the tag space and
+       find only tags matching some meaningful criteria. If given multiple
+       times, a list of patterns will be accumulated and tags matching any
+       of the patterns will be excluded. When combined with --match a tag will
+       be considered when it matches at least one --match pattern and does not
+       match any of the --exclude patterns. Use `--no-exclude` to clear and
+       reset the list of patterns.
 
 --always::
        Show uniquely abbreviated commit object as fallback.
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 abe13f3bedaf55dbefc03d26b934351107ff50af..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.
 
@@ -95,11 +101,20 @@ refname::
        The name of the ref (the part after $GIT_DIR/).
        For a non-ambiguous short name of the ref append `:short`.
        The option core.warnAmbiguousRefs is used to select the strict
-       abbreviation mode. If `strip=<N>` is appended, strips `<N>`
-       slash-separated path components from the front of the refname
-       (e.g., `%(refname:strip=2)` turns `refs/tags/foo` into `foo`.
-       `<N>` must be a positive integer.  If a displayed ref has fewer
-       components than `<N>`, the command aborts with an error.
+       abbreviation mode. If `lstrip=<N>` (`rstrip=<N>`) is appended, strips `<N>`
+       slash-separated path components from the front (back) of the refname
+       (e.g. `%(refname:lstrip=2)` turns `refs/tags/foo` into `foo` and
+       `%(refname:rstrip=2)` turns `refs/tags/foo` into `refs`).
+       If `<N>` is a negative number, strip as many path components as
+       necessary from the specified end to leave `-<N>` path components
+       (e.g. `%(refname:lstrip=-2)` turns
+       `refs/tags/foo` into `tags/foo` and `%(refname:rstrip=-1)`
+       turns `refs/tags/foo` into `refs`). When the ref does not have
+       enough components, the result becomes an empty string if
+       stripping with positive <N>, or it becomes the full refname if
+       stripping with negative <N>.  Neither is an error.
++
+`strip` can be used as a synomym to `lstrip`.
 
 objecttype::
        The type of the object (`blob`, `tree`, `commit`, `tag`).
@@ -110,21 +125,31 @@ objectsize::
 objectname::
        The object name (aka SHA-1).
        For a non-ambiguous abbreviation of the object name append `:short`.
+       For an abbreviation of the object name with desired length append
+       `:short=<length>`, where the minimum length is MINIMUM_ABBREV. The
+       length may be exceeded to ensure unique object names.
 
 upstream::
        The name of a local ref which can be considered ``upstream''
-       from the displayed ref. Respects `:short` in the same way as
-       `refname` above.  Additionally respects `:track` to show
-       "[ahead N, behind M]" and `:trackshort` to show the terse
-       version: ">" (ahead), "<" (behind), "<>" (ahead and behind),
-       or "=" (in sync).  Has no effect if the ref does not have
-       tracking information associated with it.
+       from the displayed ref. Respects `:short`, `:lstrip` and
+       `:rstrip` in the same way as `refname` above.  Additionally
+       respects `:track` to show "[ahead N, behind M]" and
+       `:trackshort` to show the terse version: ">" (ahead), "<"
+       (behind), "<>" (ahead and behind), or "=" (in sync). `:track`
+       also prints "[gone]" whenever unknown upstream ref is
+       encountered. Append `:track,nobracket` to show tracking
+       information without brackets (i.e "ahead N, behind M").  Has
+       no effect if the ref does not have tracking information
+       associated with it.  All the options apart from `nobracket`
+       are mutually exclusive, but if used together the last option
+       is selected.
 
 push::
-       The name of a local ref which represents the `@{push}` location
-       for the displayed ref. Respects `:short`, `:track`, and
-       `:trackshort` options as `upstream` does. Produces an empty
-       string if no `@{push}` ref is configured.
+       The name of a local ref which represents the `@{push}`
+       location for the displayed ref. Respects `:short`, `:lstrip`,
+       `:rstrip`, `:track`, and `:trackshort` options as `upstream`
+       does. Produces an empty string if no `@{push}` ref is
+       configured.
 
 HEAD::
        '*' if HEAD matches current ref (the checked out branch), ' '
@@ -149,6 +174,25 @@ align::
        quoted, but if nested then only the topmost level performs
        quoting.
 
+if::
+       Used as %(if)...%(then)...%(end) or
+       %(if)...%(then)...%(else)...%(end).  If there is an atom with
+       value or string literal after the %(if) then everything after
+       the %(then) is printed, else if the %(else) atom is used, then
+       everything after %(else) is printed. We ignore space when
+       evaluating the string before %(then), this is useful when we
+       use the %(HEAD) atom which prints either "*" or " " and we
+       want to apply the 'if' condition only on the 'HEAD' ref.
+       Append ":equals=<string>" or ":notequals=<string>" to compare
+       the value between the %(if:...) and %(then) atoms with the
+       given string.
+
+symref::
+       The ref which the given symbolic ref refers to. If not a
+       symbolic ref, nothing is printed. Respects the `:short`,
+       `:lstrip` and `:rstrip` options in the same way as `refname`
+       above.
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
@@ -186,6 +230,14 @@ As a special case for the date-type fields, you may specify a format for
 the date by adding `:` followed by date format name (see the
 values the `--date` option to linkgit:git-rev-list[1] takes).
 
+Some atoms like %(align) and %(if) always require a matching %(end).
+We call them "opening atoms" and sometimes denote them as %($open).
+
+When a scripting language specific quoting is in effect, everything
+between a top-level opening atom and its matching %(end) is evaluated
+according to the semantics of the opening atom and only its result
+from the top-level is quoted.
+
 
 EXAMPLES
 --------
@@ -273,6 +325,22 @@ eval=`git for-each-ref --shell --format="$fmt" \
 eval "$eval"
 ------------
 
+
+An example to show the usage of %(if)...%(then)...%(else)...%(end).
+This prefixes the current branch with a star.
+
+------------
+git for-each-ref --format="%(if)%(HEAD)%(then)* %(else)  %(end)%(refname:short)" refs/heads/
+------------
+
+
+An example to show the usage of %(if)...%(then)...%(end).
+This prints the authorname, if present.
+
+------------
+git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Authored by: %(authorname)%(end)"
+------------
+
 SEE ALSO
 --------
 linkgit:git-show-ref[1]
index f7a069bb9234850568d0870fa92ef25d0e1a19b5..c890328b02ec4c3a28aaeb8aa057d327c75b9967 100644 (file)
@@ -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 446209e2062df8d0b1c99a25eaaf4dc6f15bce3c..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
@@ -77,7 +77,8 @@ OPTIONS
        succeed.
 
 -z::
-       \0 line termination on output.
+       \0 line termination on output and do not quote filenames.
+       See OUTPUT below for more information.
 
 -x <pattern>::
 --exclude=<pattern>::
@@ -196,9 +197,10 @@ the index records up to three such pairs; one from tree O in stage
 the user (or the porcelain) to see what should eventually be recorded at the
 path. (see linkgit:git-read-tree[1] for more information on state)
 
-When `-z` option is not used, TAB, LF, and backslash characters
-in pathnames are represented as `\t`, `\n`, and `\\`,
-respectively.
+Without the `-z` option, pathnames with "unusual" characters are
+quoted as explained for the configuration variable `core.quotePath`
+(see linkgit:git-config[1]).  Using `-z` the filename is output
+verbatim and the line is terminated by a NUL byte.
 
 
 Exclude Patterns
index dbc91f98ffbee198bed7d2eeeb2758687c17adf8..9dee7bef35fb1df1afa2fcabebe22043c66b6fba 100644 (file)
@@ -53,7 +53,8 @@ OPTIONS
        Show object size of blob (file) entries.
 
 -z::
-       \0 line termination on output.
+       \0 line termination on output and do not quote filenames.
+       See OUTPUT FORMAT below for more information.
 
 --name-only::
 --name-status::
@@ -82,8 +83,6 @@ Output Format
 -------------
         <mode> SP <type> SP <object> TAB <file>
 
-Unless the `-z` option is used, TAB, LF, and backslash characters
-in pathnames are represented as `\t`, `\n`, and `\\`, respectively.
 This output format is compatible with what `--index-info --stdin` of
 'git update-index' expects.
 
@@ -95,6 +94,11 @@ Object size identified by <object> is given in bytes, and right-justified
 with minimum width of 7 characters.  Object size is given only for blobs
 (file) entries; for other entries `-` character is used in place of size.
 
+Without the `-z` option, pathnames with "unusual" characters are
+quoted as explained for the configuration variable `core.quotePath`
+(see linkgit:git-config[1]).  Using `-z` the filename is output
+verbatim and the line is terminated by a NUL byte.
+
 GIT
 ---
 Part of the linkgit:git[1] suite
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 ca28fb8e2a07bebde63c896939211fbec69e93e0..e8e68f528cf2fa5705e7cedce8d3d807fa20057d 100644 (file)
@@ -26,7 +26,18 @@ OPTIONS
 
 --refs=<pattern>::
        Only use refs whose names match a given shell pattern.  The pattern
-       can be one of branch name, tag name or fully qualified ref name.
+       can be one of branch name, tag name or fully qualified ref name. If
+       given multiple times, use refs whose names match any of the given shell
+       patterns. Use `--no-refs` to clear any previous ref patterns given.
+
+--exclude=<pattern>::
+       Do not use any ref whose name matches a given shell pattern. The
+       pattern can be one of branch name, tag name or fully qualified ref
+       name. If given multiple times, a ref will be excluded when it matches
+       any of the given patterns. When used together with --refs, a ref will
+       be used as a match only when it matches at least one --refs pattern and
+       does not match any --exclude patterns. Use `--no-exclude` to clear the
+       list of exclude patterns.
 
 --all::
        List all commits reachable from all refs
index 1624a35888c2e39f70b925a2f176d53dd02b809e..0a639664fd67f497b1597cf903015a284947509d 100644 (file)
@@ -217,6 +217,47 @@ with this feature.
 +
 "--no-force-with-lease" will cancel all the previous --force-with-lease on the
 command line.
++
+A general note on safety: supplying this option without an expected
+value, i.e. as `--force-with-lease` or `--force-with-lease=<refname>`
+interacts very badly with anything that implicitly runs `git fetch` on
+the remote to be pushed to in the background, e.g. `git fetch origin`
+on your repository in a cronjob.
++
+The protection it offers over `--force` is ensuring that subsequent
+changes your work wasn't based on aren't clobbered, but this is
+trivially defeated if some background process is updating refs in the
+background. We don't have anything except the remote tracking info to
+go by as a heuristic for refs you're expected to have seen & are
+willing to clobber.
++
+If your editor or some other system is running `git fetch` in the
+background for you a way to mitigate this is to simply set up another
+remote:
++
+       git remote add origin-push $(git config remote.origin.url)
+       git fetch origin-push
++
+Now when the background process runs `git fetch origin` the references
+on `origin-push` won't be updated, and thus commands like:
++
+       git push --force-with-lease origin-push
++
+Will fail unless you manually run `git fetch origin-push`. This method
+is of course entirely defeated by something that runs `git fetch
+--all`, in that case you'd need to either disable it or do something
+more tedious like:
++
+       git fetch              # update 'master' from remote
+       git tag base master    # mark our base point
+       git rebase -i master   # rewrite some commits
+       git push --force-with-lease=master:base master:master
++
+I.e. create a `base` tag for versions of the upstream code that you've
+seen and are willing to overwrite, then rewrite history, and finally
+force push changes to `master` if the remote version is still at
+`base`, regardless of what your local `remotes/origin/master` has been
+updated to in the background.
 
 -f::
 --force::
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 7241e968935505cdb9d507ab910b35178fcbf3bd..c40c4704486dd9d1a5650562540db7379818ab03 100644 (file)
@@ -217,6 +217,10 @@ If `$GIT_DIR` is not defined and the current directory
 is not detected to lie in a Git repository or work tree
 print a message to stderr and exit with nonzero status.
 
+--absolute-git-dir::
+       Like `--git-dir`, but its output is always the canonicalized
+       absolute path.
+
 --git-common-dir::
        Show `$GIT_COMMON_DIR` if defined, else `$GIT_DIR`.
 
@@ -257,6 +261,12 @@ print a message to stderr and exit with nonzero status.
 --show-toplevel::
        Show the absolute path of the top-level directory.
 
+--show-superproject-working-tree
+       Show the absolute path of the root of the superproject's
+       working tree (if exists) that uses the current repository as
+       its submodule.  Outputs nothing if the current repository is
+       not used as a submodule by any project.
+
 --shared-index-path::
        Show the path to the shared index file in split index mode, or
        empty if not in split-index mode.
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 2e9e344cd757674a655579c6c58569ff19a6f22e..70191d06b69ed307c6ce1d8f46c158b61eb6545f 100644 (file)
@@ -13,8 +13,11 @@ SYNOPSIS
 'git stash' drop [-q|--quiet] [<stash>]
 'git stash' ( pop | apply ) [--index] [-q|--quiet] [<stash>]
 'git stash' branch <branchname> [<stash>]
-'git stash' [save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
-            [-u|--include-untracked] [-a|--all] [<message>]]
+'git stash' save [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+            [-u|--include-untracked] [-a|--all] [<message>]
+'git stash' [push [-p|--patch] [-k|--[no-]keep-index] [-q|--quiet]
+            [-u|--include-untracked] [-a|--all] [-m|--message <message>]]
+            [--] [<pathspec>...]]
 'git stash' clear
 'git stash' create [<message>]
 'git stash' store [-m|--message <message>] [-q|--quiet] <commit>
@@ -46,14 +49,24 @@ OPTIONS
 -------
 
 save [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [<message>]::
+push [-p|--patch] [-k|--[no-]keep-index] [-u|--include-untracked] [-a|--all] [-q|--quiet] [-m|--message <message>] [--] [<pathspec>...]::
 
        Save your local modifications to a new 'stash' and roll them
        back to HEAD (in the working tree and in the index).
        The <message> part is optional and gives
-       the description along with the stashed state.  For quickly making
-       a snapshot, you can omit _both_ "save" and <message>, but giving
-       only <message> does not trigger this action to prevent a misspelled
-       subcommand from making an unwanted stash.
+       the description along with the stashed state.
++
+For quickly making a snapshot, you can omit "push".  In this mode,
+non-option arguments are not allowed to prevent a misspelled
+subcommand from making an unwanted stash.  The two exceptions to this
+are `stash -p` which acts as alias for `stash push -p` and pathspecs,
+which are allowed after a double hyphen `--` for disambiguation.
++
+When pathspec is given to 'git stash push', the new stash records the
+modified states only for the files that match the pathspec.  The index
+entries and working tree files are then rolled back to the state in
+HEAD only for these files, too, leaving files that do not match the
+pathspec intact.
 +
 If the `--keep-index` option is used, all changes already added to the
 index are left intact.
index 725065ef2d7b6b3c7c6c29a434a8996a7e1ae559..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
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -322,10 +335,9 @@ When the `-z` option is given, pathnames are printed as is and
 without any quoting and lines are terminated with a NUL (ASCII 0x00)
 byte.
 
-Otherwise, all pathnames will be "C-quoted" if they contain any tab,
-linefeed, double quote, or backslash characters. In C-quoting, these
-characters will be replaced with the corresponding C-style escape
-sequences and the resulting pathname will be double quoted.
+Without the `-z` option, pathnames with "unusual" characters are
+quoted as explained for the configuration variable `core.quotePath`
+(see linkgit:git-config[1]).
 
 
 CONFIGURATION
index 8acc72ebb8bbe956324f730612070678a9a9e188..74bc6200d564c6aa72183e393f7c7d2e0c00bf7d 100644 (file)
@@ -73,13 +73,17 @@ configuration entries unless `--name` is used to specify a logical name.
 +
 <repository> is the URL of the new submodule's origin repository.
 This may be either an absolute URL, or (if it begins with ./
-or ../), the location relative to the superproject's origin
+or ../), the location relative to the superproject's default remote
 repository (Please note that to specify a repository 'foo.git'
 which is located right next to a superproject 'bar.git', you'll
 have to use '../foo.git' instead of './foo.git' - as one might expect
 when following the rules for relative URLs - because the evaluation
 of relative URLs in Git is identical to that of relative directories).
-If the superproject doesn't have an origin configured
++
+The default remote is the remote of the remote tracking branch
+of the current branch. If no such remote tracking branch exists or
+the HEAD is detached, "origin" is assumed to be the default remote.
+If the superproject doesn't have a default remote configured
 the superproject is its own authoritative upstream and the current
 working directory is used instead.
 +
@@ -118,18 +122,26 @@ too (and can also report changes to a submodule's work tree).
 
 init [--] [<path>...]::
        Initialize the submodules recorded in the index (which were
-       added and committed elsewhere) by copying submodule
-       names and urls from .gitmodules to .git/config.
-       Optional <path> arguments limit which submodules will be initialized.
-       It will also copy the value of `submodule.$name.update` into
-       .git/config.
-       The key used in .git/config is `submodule.$name.url`.
-       This command does not alter existing information in .git/config.
-       You can then customize the submodule clone URLs in .git/config
-       for your local setup and proceed to `git submodule update`;
-       you can also just use `git submodule update --init` without
-       the explicit 'init' step if you do not intend to customize
-       any submodule locations.
+       added and committed elsewhere) by setting `submodule.$name.url`
+       in .git/config. It uses the same setting from .gitmodules as
+       a template. If the URL is relative, it will be resolved using
+       the default remote. If there is no default remote, the current
+       repository will be assumed to be upstream.
++
+Optional <path> arguments limit which submodules will be 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.
+You can then customize the submodule clone URLs in .git/config
+for your local setup and proceed to `git submodule update`;
+you can also just use `git submodule update --init` without
+the explicit 'init' step if you do not intend to customize
+any submodule locations.
++
+See the add subcommand for the defintion of default remote.
 
 deinit [-f|--force] (--all|[--] <path>...)::
        Unregister the given submodules, i.e. remove the whole
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 7386c931627f1c2bd1303407075db9831ec4045c..1579abf3c3b45f4e63f8f17746dde31a06a3a564 100644 (file)
@@ -163,14 +163,16 @@ may not support it yet.
 
 --split-index::
 --no-split-index::
-       Enable or disable split index mode. If enabled, the index is
-       split into two files, $GIT_DIR/index and $GIT_DIR/sharedindex.<SHA-1>.
-       Changes are accumulated in $GIT_DIR/index while the shared
-       index file contains all index entries stays unchanged. If
-       split-index mode is already enabled and `--split-index` is
-       given again, all changes in $GIT_DIR/index are pushed back to
-       the shared index file. This mode is designed for very large
-       indexes that take a significant amount of time to read or write.
+       Enable or disable split index mode. If split-index mode is
+       already enabled and `--split-index` is given again, all
+       changes in $GIT_DIR/index are pushed back to the shared index
+       file.
++
+These options take effect whatever the value of the `core.splitIndex`
+configuration variable (see linkgit:git-config[1]). But a warning is
+emitted when the change goes against the configured value, as the
+configured value will take effect next time the index is read and this
+will remove the intended effect of the option.
 
 --untracked-cache::
 --no-untracked-cache::
@@ -388,6 +390,31 @@ Although this bit looks similar to assume-unchanged bit, its goal is
 different from assume-unchanged bit's. Skip-worktree also takes
 precedence over assume-unchanged bit when both are set.
 
+Split index
+-----------
+
+This mode is designed for repositories with very large indexes, and
+aims at reducing the time it takes to repeatedly write these indexes.
+
+In this mode, the index is split into two files, $GIT_DIR/index and
+$GIT_DIR/sharedindex.<SHA-1>. Changes are accumulated in
+$GIT_DIR/index, the split index, while the shared index file contains
+all index entries and stays unchanged.
+
+All changes in the split index are pushed back to the shared index
+file when the number of entries in the split index reaches a level
+specified by the splitIndex.maxPercentChange config variable (see
+linkgit:git-config[1]).
+
+Each time a new shared index file is created, the old shared index
+files are deleted if their modification time is older than what is
+specified by the splitIndex.sharedIndexExpire config variable (see
+linkgit:git-config[1]).
+
+To avoid deleting a shared index file that is still used, its
+modification time is updated to the current time everytime a new split
+index based on the shared index file is either created or read from.
+
 Untracked cache
 ---------------
 
index 88c39d3ed5d5239ffd485711d01d51e546dedde6..ecc1bb4bd7c3e6adc82c9955817b532bedadc453 100644 (file)
@@ -1027,6 +1027,12 @@ Usually it is easier to configure any desired options through your
 personal `.ssh/config` file.  Please consult your ssh documentation
 for further details.
 
+`GIT_SSH_VARIANT`::
+       If this environment variable is set, it overrides Git's autodetection
+       whether `GIT_SSH`/`GIT_SSH_COMMAND`/`core.sshCommand` refer to OpenSSH,
+       plink or tortoiseplink. This variable overrides the config setting
+       `ssh.variant` that serves the same purpose.
+
 `GIT_ASKPASS`::
        If this environment variable is set, then Git commands which need to
        acquire passwords or passphrases (e.g. for HTTP or IMAP authentication)
index e0b66c1220a7d22ef5b8eb960a4e2e389a71f884..4736483865ee56c3cf23f41985e2064fbef0cfea 100644 (file)
@@ -21,9 +21,11 @@ Each line in `gitattributes` file is of form:
        pattern attr1 attr2 ...
 
 That is, a pattern followed by an attributes list,
-separated by whitespaces.  When the pattern matches the
-path in question, the attributes listed on the line are given to
-the path.
+separated by whitespaces. Leading and trailing whitespaces are
+ignored. Lines that begin with '#' are ignored. Patterns
+that begin with a double quote are quoted in C style.
+When the pattern matches the path in question, the attributes
+listed on the line are given to the path.
 
 Each attribute can be in one of these states for a given path:
 
@@ -86,7 +88,7 @@ is either not set or empty, $HOME/.config/git/attributes is used instead.
 Attributes for all users on a system should be placed in the
 `$(prefix)/etc/gitattributes` file.
 
-Sometimes you would need to override an setting of an attribute
+Sometimes you would need to override a setting of an attribute
 for a path to `Unspecified` state.  This can be done by listing
 the name of the attribute prefixed with an exclamation point `!`.
 
@@ -227,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 6f39f247a0340af2832b7a9e07cb6a0b7f6161db..db5d47eb19b8f2aa21c11a9d3eedf40b9d71e84e 100644 (file)
@@ -66,17 +66,26 @@ submodule.<name>.fetchRecurseSubmodules::
 
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
-       a submodule as modified. When set to "all", it will never be considered
-       modified (but will nonetheless show up in the output of status and
-       commit when it has been staged), "dirty" will ignore all changes
-       to the submodules work tree and
-       takes only differences between the HEAD of the submodule and the commit
-       recorded in the superproject into account. "untracked" will additionally
-       let submodules with modified tracked files in their work tree show up.
-       Using "none" (the default when this option is not set) also shows
-       submodules that have untracked files in their work tree as changed.
-       If this option is also present in the submodules entry in .git/config of
-       the superproject, the setting there will override the one found in
+       a submodule as modified. The following values are supported:
+
+       all;; The submodule will never be considered modified (but will
+           nonetheless show up in the output of status and commit when it has
+           been staged).
+
+       dirty;; All changes to the submodule's work tree will be ignored, only
+           committed differences between the HEAD of the submodule and its
+           recorded state in the superproject are taken into account.
+
+       untracked;; Only untracked files in submodules will be ignored.
+           Committed differences and modifications to tracked files will show
+           up.
+
+       none;; No modifiations to submodules are ignored, all of committed
+           differences, and modifications to tracked and untracked files are
+           shown. This is the default option.
+
+       If this option is also present in the submodules entry in .git/config
+       of the superproject, the setting there will override the one found in
        .gitmodules.
        Both settings can be overridden on the command line by using the
        "--ignore-submodule" option. The 'git submodule' commands are not
index 8ad29e61a950c2cb1f33779977a33c63ae0df33b..6e991c246915143deba887632def104fc709c9bf 100644 (file)
@@ -384,10 +384,33 @@ full pathname may have special meaning:
 +
 Glob magic is incompatible with literal magic.
 
+attr;;
+After `attr:` comes a space separated list of "attribute
+requirements", all of which must be met in order for the
+path to be considered a match; this is in addition to the
+usual non-magic pathspec pattern matching.
+See linkgit:gitattributes[5].
++
+Each of the attribute requirements for the path takes one of
+these forms:
+
+- "`ATTR`" requires that the attribute `ATTR` be set.
+
+- "`-ATTR`" requires that the attribute `ATTR` be unset.
+
+- "`ATTR=VALUE`" requires that the attribute `ATTR` be
+  set to the string `VALUE`.
+
+- "`!ATTR`" requires that the attribute `ATTR` be
+  unspecified.
++
+
 exclude;;
        After a path matches any non-exclude pathspec, it will be run
-       through all exclude pathspec (magic signature: `!`). If it
-       matches, the path is ignored.
+       through all exclude pathspec (magic signature: `!` or its
+       synonym `^`). If it matches, the path is ignored.  When there
+       is no non-exclude pathspec, the exclusion is applied to the
+       result set as if invoked without any pathspec.
 --
 
 [[def_parent]]parent::
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 260266867768a549e58e540eaa95d3bbe422e5c0..e7cbb7c13adf2f7ea3b15279a17f1a06da859cbc 100644 (file)
@@ -16,10 +16,15 @@ Data Structure
        of no interest to the calling programs.  The name of the
        attribute can be retrieved by calling `git_attr_name()`.
 
-`struct git_attr_check`::
+`struct attr_check_item`::
 
-       This structure represents a set of attributes to check in a call
-       to `git_check_attr()` function, and receives the results.
+       This structure represents one attribute and its value.
+
+`struct attr_check`::
+
+       This structure represents a collection of `attr_check_item`.
+       It is passed to `git_check_attr()` function, specifying the
+       attributes to check, and receives their values.
 
 
 Attribute Values
@@ -27,7 +32,7 @@ Attribute Values
 
 An attribute for a path can be in one of four states: Set, Unset,
 Unspecified or set to a string, and `.value` member of `struct
-git_attr_check` records it.  There are three macros to check these:
+attr_check_item` records it.  There are three macros to check these:
 
 `ATTR_TRUE()`::
 
@@ -48,49 +53,51 @@ value of the attribute for the path.
 Querying Specific Attributes
 ----------------------------
 
-* Prepare an array of `struct git_attr_check` to define the list of
-  attributes you would want to check.  To populate this array, you would
-  need to define necessary attributes by calling `git_attr()` function.
+* Prepare `struct attr_check` using attr_check_initl()
+  function, enumerating the names of attributes whose values you are
+  interested in, terminated with a NULL pointer.  Alternatively, an
+  empty `struct attr_check` can be prepared by calling
+  `attr_check_alloc()` function and then attributes you want to
+  ask about can be added to it with `attr_check_append()`
+  function.
 
 * Call `git_check_attr()` to check the attributes for the path.
 
-* Inspect `git_attr_check` structure to see how each of the attribute in
-  the array is defined for the path.
+* Inspect `attr_check` structure to see how each of the
+  attribute in the array is defined for the path.
 
 
 Example
 -------
 
-To see how attributes "crlf" and "indent" are set for different paths.
+To see how attributes "crlf" and "ident" are set for different paths.
 
-. Prepare an array of `struct git_attr_check` with two elements (because
-  we are checking two attributes).  Initialize their `attr` member with
-  pointers to `struct git_attr` obtained by calling `git_attr()`:
+. Prepare a `struct attr_check` with two elements (because
+  we are checking two attributes):
 
 ------------
-static struct git_attr_check check[2];
+static struct attr_check *check;
 static void setup_check(void)
 {
-       if (check[0].attr)
+       if (check)
                return; /* already done */
-       check[0].attr = git_attr("crlf");
-       check[1].attr = git_attr("ident");
+       check = attr_check_initl("crlf", "ident", NULL);
 }
 ------------
 
-. Call `git_check_attr()` with the prepared array of `struct git_attr_check`:
+. Call `git_check_attr()` with the prepared `struct attr_check`:
 
 ------------
        const char *path;
 
        setup_check();
-       git_check_attr(path, ARRAY_SIZE(check), check);
+       git_check_attr(path, check);
 ------------
 
-. Act on `.value` member of the result, left in `check[]`:
+. Act on `.value` member of the result, left in `check->items[]`:
 
 ------------
-       const char *value = check[0].value;
+       const char *value = check->items[0].value;
 
        if (ATTR_TRUE(value)) {
                The attribute is Set, by listing only the name of the
@@ -109,20 +116,39 @@ static void setup_check(void)
        }
 ------------
 
+To see how attributes in argv[] are set for different paths, only
+the first step in the above would be different.
+
+------------
+static struct attr_check *check;
+static void setup_check(const char **argv)
+{
+       check = attr_check_alloc();
+       while (*argv) {
+               struct git_attr *attr = git_attr(*argv);
+               attr_check_append(check, attr);
+               argv++;
+       }
+}
+------------
+
 
 Querying All Attributes
 -----------------------
 
 To get the values of all attributes associated with a file:
 
-* Call `git_all_attrs()`, which returns an array of `git_attr_check`
-  structures.
+* Prepare an empty `attr_check` structure by calling
+  `attr_check_alloc()`.
+
+* Call `git_all_attrs()`, which populates the `attr_check`
+  with the attributes attached to the path.
 
-* Iterate over the `git_attr_check` array to examine the attribute
-  names and values.  The name of the attribute described by a
-  `git_attr_check` object can be retrieved via
-  `git_attr_name(check[i].attr)`.  (Please note that no items will be
-  returned for unset attributes, so `ATTR_UNSET()` will return false
-  for all returned `git_array_check` objects.)
+* Iterate over the `attr_check.items[]` array to examine
+  the attribute names and values.  The name of the attribute
+  described by a  `attr_check.items[]` object can be retrieved via
+  `git_attr_name(check->items[i].attr)`.  (Please note that no items
+  will be returned for unset attributes, so `ATTR_UNSET()` will return
+  false for all returned `attr_check.items[]` objects.)
 
-* Free the `git_array_check` array.
+* Free the `attr_check` struct by calling `attr_check_free()`.
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);
+}
+-----------------------------------------
index 27bd701c0d6862f38c700fbfc02c24b135bd6c0e..36768b479e16c9456982230f078d98513a72c37f 100644 (file)
@@ -168,6 +168,11 @@ There are some macros to easily define options:
        Introduce an option with string argument.
        The string argument is put into `str_var`.
 
+`OPT_STRING_LIST(short, long, &struct string_list, arg_str, description)`::
+       Introduce an option with string argument.
+       The string argument is stored as an element in `string_list`.
+       Use of `--no-option` will clear the list of preceding values.
+
 `OPT_INTEGER(short, long, &int_var, description)`::
        Introduce an option with integer argument.
        The integer is put into `int_var`.
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 e83f591a7bc1f525627557c59648da579594c1de..e681ede9d8b825569cfe2b39ec02fddca3a8fe40 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.12.2
+DEF_VER=v2.13.0-rc0
 
 LF='
 '
index d5454ca33632f8027b06399acc56b7163074b056..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
@@ -779,6 +789,7 @@ LIB_OBJS += notes-cache.o
 LIB_OBJS += notes-merge.o
 LIB_OBJS += notes-utils.o
 LIB_OBJS += object.o
+LIB_OBJS += oidset.o
 LIB_OBJS += pack-bitmap.o
 LIB_OBJS += pack-bitmap-write.o
 LIB_OBJS += pack-check.o
@@ -930,6 +941,7 @@ BUILTIN_OBJS += builtin/prune.o
 BUILTIN_OBJS += builtin/pull.o
 BUILTIN_OBJS += builtin/push.o
 BUILTIN_OBJS += builtin/read-tree.o
+BUILTIN_OBJS += builtin/rebase--helper.o
 BUILTIN_OBJS += builtin/receive-pack.o
 BUILTIN_OBJS += builtin/reflog.o
 BUILTIN_OBJS += builtin/remote.o
@@ -1381,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
@@ -1586,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))
 
@@ -1619,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
@@ -1836,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) $@+
 
@@ -2223,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
@@ -2332,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 a04927d3a6353fff040b32addf6d7208e63c032b..125bf78f3b9ed2f1444e1873ed02cce9f0f4c5b8 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.12.3.txt
\ No newline at end of file
+Documentation/RelNotes/2.13.0.txt
\ No newline at end of file
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 01751e574bc10c1211d0253d215917ecc4ce82dd..60b889198656f42a1d073503c4da5886bf0a0526 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -87,19 +87,6 @@ void *sha1_file_to_archive(const struct archiver_args *args,
        return buffer;
 }
 
-static void setup_archive_check(struct git_attr_check *check)
-{
-       static struct git_attr *attr_export_ignore;
-       static struct git_attr *attr_export_subst;
-
-       if (!attr_export_ignore) {
-               attr_export_ignore = git_attr("export-ignore");
-               attr_export_subst = git_attr("export-subst");
-       }
-       check[0].attr = attr_export_ignore;
-       check[1].attr = attr_export_subst;
-}
-
 struct directory {
        struct directory *up;
        struct object_id oid;
@@ -120,10 +107,10 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                void *context)
 {
        static struct strbuf path = STRBUF_INIT;
+       static struct attr_check *check;
        struct archiver_context *c = context;
        struct archiver_args *args = c->args;
        write_archive_entry_fn_t write_entry = c->write_entry;
-       struct git_attr_check check[2];
        const char *path_without_prefix;
        int err;
 
@@ -137,11 +124,12 @@ static int write_archive_entry(const unsigned char *sha1, const char *base,
                strbuf_addch(&path, '/');
        path_without_prefix = path.buf + args->baselen;
 
-       setup_archive_check(check);
-       if (!git_check_attr(path_without_prefix, ARRAY_SIZE(check), check)) {
-               if (ATTR_TRUE(check[0].value))
+       if (!check)
+               check = attr_check_initl("export-ignore", "export-subst", NULL);
+       if (!git_check_attr(path_without_prefix, check)) {
+               if (ATTR_TRUE(check->items[0].value))
                        return 0;
-               args->convert = ATTR_TRUE(check[1].value);
+               args->convert = ATTR_TRUE(check->items[1].value);
        }
 
        if (S_ISDIR(mode) || S_ISGITLINK(mode)) {
diff --git a/attr.c b/attr.c
index 1fcf042b87c8a39fb967e07c5acb81c0c364d136..7e2134471cb4afc1b891208ebe4bebd6ad55571d 100644 (file)
--- a/attr.c
+++ b/attr.c
@@ -13,6 +13,8 @@
 #include "attr.h"
 #include "dir.h"
 #include "utf8.h"
+#include "quote.h"
+#include "thread-utils.h"
 
 const char git_attr__true[] = "(builtin)true";
 const char git_attr__false[] = "\0(builtin)false";
@@ -22,99 +24,234 @@ static const char git_attr__unknown[] = "(builtin)unknown";
 #define ATTR__UNSET NULL
 #define ATTR__UNKNOWN git_attr__unknown
 
-/* This is a randomly chosen prime. */
-#define HASHSIZE 257
-
 #ifndef DEBUG_ATTR
 #define DEBUG_ATTR 0
 #endif
 
 struct git_attr {
-       struct git_attr *next;
-       unsigned h;
-       int attr_nr;
-       int maybe_macro;
-       int maybe_real;
-       char name[FLEX_ARRAY];
+       int attr_nr; /* unique attribute number */
+       char name[FLEX_ARRAY]; /* attribute name */
 };
-static int attr_nr;
-static int cannot_trust_maybe_real;
-
-static struct git_attr_check *check_all_attr;
-static struct git_attr *(git_attr_hash[HASHSIZE]);
 
-char *git_attr_name(struct git_attr *attr)
+const char *git_attr_name(const struct git_attr *attr)
 {
        return attr->name;
 }
 
-static unsigned hash_name(const char *name, int namelen)
+struct attr_hashmap {
+       struct hashmap map;
+#ifndef NO_PTHREADS
+       pthread_mutex_t mutex;
+#endif
+};
+
+static inline void hashmap_lock(struct attr_hashmap *map)
 {
-       unsigned val = 0, c;
+#ifndef NO_PTHREADS
+       pthread_mutex_lock(&map->mutex);
+#endif
+}
 
-       while (namelen--) {
-               c = *name++;
-               val = ((val << 7) | (val >> 22)) ^ c;
+static inline void hashmap_unlock(struct attr_hashmap *map)
+{
+#ifndef NO_PTHREADS
+       pthread_mutex_unlock(&map->mutex);
+#endif
+}
+
+/*
+ * The global dictionary of all interned attributes.  This
+ * is a singleton object which is shared between threads.
+ * Access to this dictionary must be surrounded with a mutex.
+ */
+static struct attr_hashmap g_attr_hashmap;
+
+/* The container for objects stored in "struct attr_hashmap" */
+struct attr_hash_entry {
+       struct hashmap_entry ent; /* must be the first member! */
+       const char *key; /* the key; memory should be owned by value */
+       size_t keylen; /* length of the key */
+       void *value; /* the stored value */
+};
+
+/* attr_hashmap comparison function */
+static int attr_hash_entry_cmp(const struct attr_hash_entry *a,
+                              const struct attr_hash_entry *b,
+                              void *unused)
+{
+       return (a->keylen != b->keylen) || strncmp(a->key, b->key, a->keylen);
+}
+
+/* Initialize an 'attr_hashmap' object */
+static void attr_hashmap_init(struct attr_hashmap *map)
+{
+       hashmap_init(&map->map, (hashmap_cmp_fn) attr_hash_entry_cmp, 0);
+}
+
+/*
+ * Retrieve the 'value' stored in a hashmap given the provided 'key'.
+ * If there is no matching entry, return NULL.
+ */
+static void *attr_hashmap_get(struct attr_hashmap *map,
+                             const char *key, size_t keylen)
+{
+       struct attr_hash_entry k;
+       struct attr_hash_entry *e;
+
+       if (!map->map.tablesize)
+               attr_hashmap_init(map);
+
+       hashmap_entry_init(&k, memhash(key, keylen));
+       k.key = key;
+       k.keylen = keylen;
+       e = hashmap_get(&map->map, &k, NULL);
+
+       return e ? e->value : NULL;
+}
+
+/* Add 'value' to a hashmap based on the provided 'key'. */
+static void attr_hashmap_add(struct attr_hashmap *map,
+                            const char *key, size_t keylen,
+                            void *value)
+{
+       struct attr_hash_entry *e;
+
+       if (!map->map.tablesize)
+               attr_hashmap_init(map);
+
+       e = xmalloc(sizeof(struct attr_hash_entry));
+       hashmap_entry_init(e, memhash(key, keylen));
+       e->key = key;
+       e->keylen = keylen;
+       e->value = value;
+
+       hashmap_add(&map->map, e);
+}
+
+struct all_attrs_item {
+       const struct git_attr *attr;
+       const char *value;
+       /*
+        * If 'macro' is non-NULL, indicates that 'attr' is a macro based on
+        * the current attribute stack and contains a pointer to the match_attr
+        * definition of the macro
+        */
+       const struct match_attr *macro;
+};
+
+/*
+ * Reallocate and reinitialize the array of all attributes (which is used in
+ * the attribute collection process) in 'check' based on the global dictionary
+ * of attributes.
+ */
+static void all_attrs_init(struct attr_hashmap *map, struct attr_check *check)
+{
+       int i;
+
+       hashmap_lock(map);
+
+       if (map->map.size < check->all_attrs_nr)
+               die("BUG: interned attributes shouldn't be deleted");
+
+       /*
+        * If the number of attributes in the global dictionary has increased
+        * (or this attr_check instance doesn't have an initialized all_attrs
+        * field), reallocate the provided attr_check instance's all_attrs
+        * field and fill each entry with its corresponding git_attr.
+        */
+       if (map->map.size != check->all_attrs_nr) {
+               struct attr_hash_entry *e;
+               struct hashmap_iter iter;
+               hashmap_iter_init(&map->map, &iter);
+
+               REALLOC_ARRAY(check->all_attrs, map->map.size);
+               check->all_attrs_nr = map->map.size;
+
+               while ((e = hashmap_iter_next(&iter))) {
+                       const struct git_attr *a = e->value;
+                       check->all_attrs[a->attr_nr].attr = a;
+               }
+       }
+
+       hashmap_unlock(map);
+
+       /*
+        * Re-initialize every entry in check->all_attrs.
+        * This re-initialization can live outside of the locked region since
+        * the attribute dictionary is no longer being accessed.
+        */
+       for (i = 0; i < check->all_attrs_nr; i++) {
+               check->all_attrs[i].value = ATTR__UNKNOWN;
+               check->all_attrs[i].macro = NULL;
        }
-       return val;
 }
 
-static int invalid_attr_name(const char *name, int namelen)
+static int attr_name_valid(const char *name, size_t namelen)
 {
        /*
         * Attribute name cannot begin with '-' and must consist of
         * characters from [-A-Za-z0-9_.].
         */
        if (namelen <= 0 || *name == '-')
-               return -1;
+               return 0;
        while (namelen--) {
                char ch = *name++;
                if (! (ch == '-' || ch == '.' || ch == '_' ||
                       ('0' <= ch && ch <= '9') ||
                       ('a' <= ch && ch <= 'z') ||
                       ('A' <= ch && ch <= 'Z')) )
-                       return -1;
+                       return 0;
        }
-       return 0;
+       return 1;
+}
+
+static void report_invalid_attr(const char *name, size_t len,
+                               const char *src, int lineno)
+{
+       struct strbuf err = STRBUF_INIT;
+       strbuf_addf(&err, _("%.*s is not a valid attribute name"),
+                   (int) len, name);
+       fprintf(stderr, "%s: %s:%d\n", err.buf, src, lineno);
+       strbuf_release(&err);
 }
 
-static struct git_attr *git_attr_internal(const char *name, int len)
+/*
+ * Given a 'name', lookup and return the corresponding attribute in the global
+ * dictionary.  If no entry is found, create a new attribute and store it in
+ * the dictionary.
+ */
+static const struct git_attr *git_attr_internal(const char *name, int namelen)
 {
-       unsigned hval = hash_name(name, len);
-       unsigned pos = hval % HASHSIZE;
        struct git_attr *a;
 
-       for (a = git_attr_hash[pos]; a; a = a->next) {
-               if (a->h == hval &&
-                   !memcmp(a->name, name, len) && !a->name[len])
-                       return a;
+       if (!attr_name_valid(name, namelen))
+               return NULL;
+
+       hashmap_lock(&g_attr_hashmap);
+
+       a = attr_hashmap_get(&g_attr_hashmap, name, namelen);
+
+       if (!a) {
+               FLEX_ALLOC_MEM(a, name, name, namelen);
+               a->attr_nr = g_attr_hashmap.map.size;
+
+               attr_hashmap_add(&g_attr_hashmap, a->name, namelen, a);
+               assert(a->attr_nr == (g_attr_hashmap.map.size - 1));
        }
 
-       if (invalid_attr_name(name, len))
-               return NULL;
+       hashmap_unlock(&g_attr_hashmap);
 
-       FLEX_ALLOC_MEM(a, name, name, len);
-       a->h = hval;
-       a->next = git_attr_hash[pos];
-       a->attr_nr = attr_nr++;
-       a->maybe_macro = 0;
-       a->maybe_real = 0;
-       git_attr_hash[pos] = a;
-
-       REALLOC_ARRAY(check_all_attr, attr_nr);
-       check_all_attr[a->attr_nr].attr = a;
-       check_all_attr[a->attr_nr].value = ATTR__UNKNOWN;
        return a;
 }
 
-struct git_attr *git_attr(const char *name)
+const struct git_attr *git_attr(const char *name)
 {
        return git_attr_internal(name, strlen(name));
 }
 
 /* What does a matched pattern decide? */
 struct attr_state {
-       struct git_attr *attr;
+       const struct git_attr *attr;
        const char *setto;
 };
 
@@ -131,9 +268,8 @@ struct pattern {
  * If is_macro is true, then u.attr is a pointer to the git_attr being
  * defined.
  *
- * If is_macro is false, then u.pattern points at the filename pattern
- * to which the rule applies.  (The memory pointed to is part of the
- * memory block allocated for the match_attr instance.)
+ * If is_macro is false, then u.pat is the filename pattern to which the
+ * rule applies.
  *
  * In either case, num_attr is the number of attributes affected by
  * this rule, and state is an array listing them.  The attributes are
@@ -142,7 +278,7 @@ struct pattern {
 struct match_attr {
        union {
                struct pattern pat;
-               struct git_attr *attr;
+               const struct git_attr *attr;
        } u;
        char is_macro;
        unsigned num_attr;
@@ -177,13 +313,17 @@ static const char *parse_attr(const char *src, int lineno, const char *cp,
                        cp++;
                        len--;
                }
-               if (invalid_attr_name(cp, len)) {
-                       fprintf(stderr,
-                               "%.*s is not a valid attribute name: %s:%d\n",
-                               len, cp, src, lineno);
+               if (!attr_name_valid(cp, len)) {
+                       report_invalid_attr(cp, len, src, lineno);
                        return NULL;
                }
        } else {
+               /*
+                * As this function is always called twice, once with
+                * e == NULL in the first pass and then e != NULL in
+                * the second pass, no need for attr_name_valid()
+                * check here.
+                */
                if (*cp == '-' || *cp == '!') {
                        e->setto = (*cp == '-') ? ATTR__FALSE : ATTR__UNSET;
                        cp++;
@@ -207,41 +347,47 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
        const char *cp, *name, *states;
        struct match_attr *res = NULL;
        int is_macro;
+       struct strbuf pattern = STRBUF_INIT;
 
        cp = line + strspn(line, blank);
        if (!*cp || *cp == '#')
                return NULL;
        name = cp;
-       namelen = strcspn(name, blank);
+
+       if (*cp == '"' && !unquote_c_style(&pattern, name, &states)) {
+               name = pattern.buf;
+               namelen = pattern.len;
+       } else {
+               namelen = strcspn(name, blank);
+               states = name + namelen;
+       }
+
        if (strlen(ATTRIBUTE_MACRO_PREFIX) < namelen &&
            starts_with(name, ATTRIBUTE_MACRO_PREFIX)) {
                if (!macro_ok) {
                        fprintf(stderr, "%s not allowed: %s:%d\n",
                                name, src, lineno);
-                       return NULL;
+                       goto fail_return;
                }
                is_macro = 1;
                name += strlen(ATTRIBUTE_MACRO_PREFIX);
                name += strspn(name, blank);
                namelen = strcspn(name, blank);
-               if (invalid_attr_name(name, namelen)) {
-                       fprintf(stderr,
-                               "%.*s is not a valid attribute name: %s:%d\n",
-                               namelen, name, src, lineno);
-                       return NULL;
+               if (!attr_name_valid(name, namelen)) {
+                       report_invalid_attr(name, namelen, src, lineno);
+                       goto fail_return;
                }
        }
        else
                is_macro = 0;
 
-       states = name + namelen;
        states += strspn(states, blank);
 
        /* First pass to count the attr_states */
        for (cp = states, num_attr = 0; *cp; num_attr++) {
                cp = parse_attr(src, lineno, cp, NULL);
                if (!cp)
-                       return NULL;
+                       goto fail_return;
        }
 
        res = xcalloc(1,
@@ -250,7 +396,6 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
                      (is_macro ? 0 : namelen + 1));
        if (is_macro) {
                res->u.attr = git_attr_internal(name, namelen);
-               res->u.attr->maybe_macro = 1;
        } else {
                char *p = (char *)&(res->state[num_attr]);
                memcpy(p, name, namelen);
@@ -262,7 +407,7 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
                if (res->u.pat.flags & EXC_FLAG_NEGATIVE) {
                        warning(_("Negative patterns are ignored in git attributes\n"
                                  "Use '\\!' for literal leading exclamation."));
-                       return NULL;
+                       goto fail_return;
                }
        }
        res->is_macro = is_macro;
@@ -271,13 +416,15 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
        /* Second pass to fill the attr_states */
        for (cp = states, i = 0; *cp; i++) {
                cp = parse_attr(src, lineno, cp, &(res->state[i]));
-               if (!is_macro)
-                       res->state[i].attr->maybe_real = 1;
-               if (res->state[i].attr->maybe_macro)
-                       cannot_trust_maybe_real = 1;
        }
 
+       strbuf_release(&pattern);
        return res;
+
+fail_return:
+       strbuf_release(&pattern);
+       free(res);
+       return NULL;
 }
 
 /*
@@ -295,19 +442,19 @@ static struct match_attr *parse_attr_line(const char *line, const char *src,
  * directory (again, reading the file from top to bottom) down to the
  * current directory, and then scan the list backwards to find the first match.
  * This is exactly the same as what is_excluded() does in dir.c to deal with
- * .gitignore
+ * .gitignore file and info/excludes file as a fallback.
  */
 
-static struct attr_stack {
+struct attr_stack {
        struct attr_stack *prev;
        char *origin;
        size_t originlen;
        unsigned num_matches;
        unsigned alloc;
        struct match_attr **attrs;
-} *attr_stack;
+};
 
-static void free_attr_elem(struct attr_stack *e)
+static void attr_stack_free(struct attr_stack *e)
 {
        int i;
        free(e->origin);
@@ -330,6 +477,190 @@ static void free_attr_elem(struct attr_stack *e)
        free(e);
 }
 
+static void drop_attr_stack(struct attr_stack **stack)
+{
+       while (*stack) {
+               struct attr_stack *elem = *stack;
+               *stack = elem->prev;
+               attr_stack_free(elem);
+       }
+}
+
+/* List of all attr_check structs; access should be surrounded by mutex */
+static struct check_vector {
+       size_t nr;
+       size_t alloc;
+       struct attr_check **checks;
+#ifndef NO_PTHREADS
+       pthread_mutex_t mutex;
+#endif
+} check_vector;
+
+static inline void vector_lock(void)
+{
+#ifndef NO_PTHREADS
+       pthread_mutex_lock(&check_vector.mutex);
+#endif
+}
+
+static inline void vector_unlock(void)
+{
+#ifndef NO_PTHREADS
+       pthread_mutex_unlock(&check_vector.mutex);
+#endif
+}
+
+static void check_vector_add(struct attr_check *c)
+{
+       vector_lock();
+
+       ALLOC_GROW(check_vector.checks,
+                  check_vector.nr + 1,
+                  check_vector.alloc);
+       check_vector.checks[check_vector.nr++] = c;
+
+       vector_unlock();
+}
+
+static void check_vector_remove(struct attr_check *check)
+{
+       int i;
+
+       vector_lock();
+
+       /* Find entry */
+       for (i = 0; i < check_vector.nr; i++)
+               if (check_vector.checks[i] == check)
+                       break;
+
+       if (i >= check_vector.nr)
+               die("BUG: no entry found");
+
+       /* shift entries over */
+       for (; i < check_vector.nr - 1; i++)
+               check_vector.checks[i] = check_vector.checks[i + 1];
+
+       check_vector.nr--;
+
+       vector_unlock();
+}
+
+/* Iterate through all attr_check instances and drop their stacks */
+static void drop_all_attr_stacks(void)
+{
+       int i;
+
+       vector_lock();
+
+       for (i = 0; i < check_vector.nr; i++) {
+               drop_attr_stack(&check_vector.checks[i]->stack);
+       }
+
+       vector_unlock();
+}
+
+struct attr_check *attr_check_alloc(void)
+{
+       struct attr_check *c = xcalloc(1, sizeof(struct attr_check));
+
+       /* save pointer to the check struct */
+       check_vector_add(c);
+
+       return c;
+}
+
+struct attr_check *attr_check_initl(const char *one, ...)
+{
+       struct attr_check *check;
+       int cnt;
+       va_list params;
+       const char *param;
+
+       va_start(params, one);
+       for (cnt = 1; (param = va_arg(params, const char *)) != NULL; cnt++)
+               ;
+       va_end(params);
+
+       check = attr_check_alloc();
+       check->nr = cnt;
+       check->alloc = cnt;
+       check->items = xcalloc(cnt, sizeof(struct attr_check_item));
+
+       check->items[0].attr = git_attr(one);
+       va_start(params, one);
+       for (cnt = 1; cnt < check->nr; cnt++) {
+               const struct git_attr *attr;
+               param = va_arg(params, const char *);
+               if (!param)
+                       die("BUG: counted %d != ended at %d",
+                           check->nr, cnt);
+               attr = git_attr(param);
+               if (!attr)
+                       die("BUG: %s: not a valid attribute name", param);
+               check->items[cnt].attr = attr;
+       }
+       va_end(params);
+       return check;
+}
+
+struct attr_check *attr_check_dup(const struct attr_check *check)
+{
+       struct attr_check *ret;
+
+       if (!check)
+               return NULL;
+
+       ret = attr_check_alloc();
+
+       ret->nr = check->nr;
+       ret->alloc = check->alloc;
+       ALLOC_ARRAY(ret->items, ret->nr);
+       COPY_ARRAY(ret->items, check->items, ret->nr);
+
+       return ret;
+}
+
+struct attr_check_item *attr_check_append(struct attr_check *check,
+                                         const struct git_attr *attr)
+{
+       struct attr_check_item *item;
+
+       ALLOC_GROW(check->items, check->nr + 1, check->alloc);
+       item = &check->items[check->nr++];
+       item->attr = attr;
+       return item;
+}
+
+void attr_check_reset(struct attr_check *check)
+{
+       check->nr = 0;
+}
+
+void attr_check_clear(struct attr_check *check)
+{
+       free(check->items);
+       check->items = NULL;
+       check->alloc = 0;
+       check->nr = 0;
+
+       free(check->all_attrs);
+       check->all_attrs = NULL;
+       check->all_attrs_nr = 0;
+
+       drop_attr_stack(&check->stack);
+}
+
+void attr_check_free(struct attr_check *check)
+{
+       if (check) {
+               /* Remove check from the check vector */
+               check_vector_remove(check);
+
+               attr_check_clear(check);
+               free(check);
+       }
+}
+
 static const char *builtin_attr[] = {
        "[attr]binary -diff -merge -text",
        NULL,
@@ -362,9 +693,31 @@ static struct attr_stack *read_attr_from_array(const char **list)
        return res;
 }
 
+/*
+ * Callers into the attribute system assume there is a single, system-wide
+ * global state where attributes are read from and when the state is flipped by
+ * calling git_attr_set_direction(), the stack frames that have been
+ * constructed need to be discarded so so that subsequent calls into the
+ * attribute system will lazily read from the right place.  Since changing
+ * direction causes a global paradigm shift, it should not ever be called while
+ * another thread could potentially be calling into the attribute system.
+ */
 static enum git_attr_direction direction;
 static struct index_state *use_index;
 
+void git_attr_set_direction(enum git_attr_direction new_direction,
+                           struct index_state *istate)
+{
+       if (is_bare_repository() && new_direction != GIT_ATTR_INDEX)
+               die("BUG: non-INDEX attr direction in a bare repo");
+
+       if (new_direction != direction)
+               drop_all_attr_stacks();
+
+       direction = new_direction;
+       use_index = istate;
+}
+
 static struct attr_stack *read_attr_from_file(const char *path, int macro_ok)
 {
        FILE *fp = fopen(path, "r");
@@ -402,8 +755,8 @@ static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
        for (sp = buf; *sp; ) {
                char *ep;
                int more;
-               for (ep = sp; *ep && *ep != '\n'; ep++)
-                       ;
+
+               ep = strchrnul(sp, '\n');
                more = (*ep == '\n');
                *ep = '\0';
                handle_attr_line(res, sp, path, ++lineno, macro_ok);
@@ -415,25 +768,28 @@ static struct attr_stack *read_attr_from_index(const char *path, int macro_ok)
 
 static struct attr_stack *read_attr(const char *path, int macro_ok)
 {
-       struct attr_stack *res;
+       struct attr_stack *res = NULL;
 
-       if (direction == GIT_ATTR_CHECKOUT) {
+       if (direction == GIT_ATTR_INDEX) {
                res = read_attr_from_index(path, macro_ok);
-               if (!res)
-                       res = read_attr_from_file(path, macro_ok);
-       }
-       else if (direction == GIT_ATTR_CHECKIN) {
-               res = read_attr_from_file(path, macro_ok);
-               if (!res)
-                       /*
-                        * There is no checked out .gitattributes file there, but
-                        * we might have it in the index.  We allow operation in a
-                        * sparsely checked out work tree, so read from it.
-                        */
+       } else if (!is_bare_repository()) {
+               if (direction == GIT_ATTR_CHECKOUT) {
                        res = read_attr_from_index(path, macro_ok);
+                       if (!res)
+                               res = read_attr_from_file(path, macro_ok);
+               } else if (direction == GIT_ATTR_CHECKIN) {
+                       res = read_attr_from_file(path, macro_ok);
+                       if (!res)
+                               /*
+                                * There is no checked out .gitattributes file
+                                * there, but we might have it in the index.
+                                * We allow operation in a sparsely checked out
+                                * work tree, so read from it.
+                                */
+                               res = read_attr_from_index(path, macro_ok);
+               }
        }
-       else
-               res = read_attr_from_index(path, macro_ok);
+
        if (!res)
                res = xcalloc(1, sizeof(*res));
        return res;
@@ -464,16 +820,7 @@ static void debug_set(const char *what, const char *match, struct git_attr *attr
 #define debug_push(a) do { ; } while (0)
 #define debug_pop(a) do { ; } while (0)
 #define debug_set(a,b,c,d) do { ; } while (0)
-#endif
-
-static void drop_attr_stack(void)
-{
-       while (attr_stack) {
-               struct attr_stack *elem = attr_stack;
-               attr_stack = elem->prev;
-               free_attr_elem(elem);
-       }
-}
+#endif /* DEBUG_ATTR */
 
 static const char *git_etc_gitattributes(void)
 {
@@ -483,6 +830,14 @@ static const char *git_etc_gitattributes(void)
        return system_wide;
 }
 
+static const char *get_home_gitattributes(void)
+{
+       if (!git_attributes_file)
+               git_attributes_file = xdg_config_home("attributes");
+
+       return git_attributes_file;
+}
+
 static int git_attr_system(void)
 {
        return !git_env_bool("GIT_ATTR_NOSYSTEM", 0);
@@ -490,64 +845,60 @@ static int git_attr_system(void)
 
 static GIT_PATH_FUNC(git_path_info_attributes, INFOATTRIBUTES_FILE)
 
-static void bootstrap_attr_stack(void)
+static void push_stack(struct attr_stack **attr_stack_p,
+                      struct attr_stack *elem, char *origin, size_t originlen)
 {
-       struct attr_stack *elem;
+       if (elem) {
+               elem->origin = origin;
+               if (origin)
+                       elem->originlen = originlen;
+               elem->prev = *attr_stack_p;
+               *attr_stack_p = elem;
+       }
+}
 
-       if (attr_stack)
+static void bootstrap_attr_stack(struct attr_stack **stack)
+{
+       struct attr_stack *e;
+
+       if (*stack)
                return;
 
-       elem = read_attr_from_array(builtin_attr);
-       elem->origin = NULL;
-       elem->prev = attr_stack;
-       attr_stack = elem;
+       /* builtin frame */
+       e = read_attr_from_array(builtin_attr);
+       push_stack(stack, e, NULL, 0);
 
+       /* system-wide frame */
        if (git_attr_system()) {
-               elem = read_attr_from_file(git_etc_gitattributes(), 1);
-               if (elem) {
-                       elem->origin = NULL;
-                       elem->prev = attr_stack;
-                       attr_stack = elem;
-               }
+               e = read_attr_from_file(git_etc_gitattributes(), 1);
+               push_stack(stack, e, NULL, 0);
        }
 
-       if (!git_attributes_file)
-               git_attributes_file = xdg_config_home("attributes");
-       if (git_attributes_file) {
-               elem = read_attr_from_file(git_attributes_file, 1);
-               if (elem) {
-                       elem->origin = NULL;
-                       elem->prev = attr_stack;
-                       attr_stack = elem;
-               }
+       /* home directory */
+       if (get_home_gitattributes()) {
+               e = read_attr_from_file(get_home_gitattributes(), 1);
+               push_stack(stack, e, NULL, 0);
        }
 
-       if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
-               elem = read_attr(GITATTRIBUTES_FILE, 1);
-               elem->origin = xstrdup("");
-               elem->originlen = 0;
-               elem->prev = attr_stack;
-               attr_stack = elem;
-               debug_push(elem);
-       }
+       /* root directory */
+       e = read_attr(GITATTRIBUTES_FILE, 1);
+       push_stack(stack, e, xstrdup(""), 0);
 
+       /* info frame */
        if (startup_info->have_repository)
-               elem = read_attr_from_file(git_path_info_attributes(), 1);
+               e = read_attr_from_file(git_path_info_attributes(), 1);
        else
-               elem = NULL;
-
-       if (!elem)
-               elem = xcalloc(1, sizeof(*elem));
-       elem->origin = NULL;
-       elem->prev = attr_stack;
-       attr_stack = elem;
+               e = NULL;
+       if (!e)
+               e = xcalloc(1, sizeof(struct attr_stack));
+       push_stack(stack, e, NULL, 0);
 }
 
-static void prepare_attr_stack(const char *path, int dirlen)
+static void prepare_attr_stack(const char *path, int dirlen,
+                              struct attr_stack **stack)
 {
-       struct attr_stack *elem, *info;
-       int len;
-       const char *cp;
+       struct attr_stack *info;
+       struct strbuf pathbuf = STRBUF_INIT;
 
        /*
         * At the bottom of the attribute stack is the built-in
@@ -564,13 +915,13 @@ static void prepare_attr_stack(const char *path, int dirlen)
         * .gitattributes in deeper directories to shallower ones,
         * and finally use the built-in set as the default.
         */
-       bootstrap_attr_stack();
+       bootstrap_attr_stack(stack);
 
        /*
         * Pop the "info" one that is always at the top of the stack.
         */
-       info = attr_stack;
-       attr_stack = info->prev;
+       info = *stack;
+       *stack = info->prev;
 
        /*
         * Pop the ones from directories that are not the prefix of
@@ -578,59 +929,63 @@ static void prepare_attr_stack(const char *path, int dirlen)
         * the root one (whose origin is an empty string "") or the builtin
         * one (whose origin is NULL) without popping it.
         */
-       while (attr_stack->origin) {
-               int namelen = strlen(attr_stack->origin);
+       while ((*stack)->origin) {
+               int namelen = (*stack)->originlen;
+               struct attr_stack *elem;
 
-               elem = attr_stack;
+               elem = *stack;
                if (namelen <= dirlen &&
                    !strncmp(elem->origin, path, namelen) &&
                    (!namelen || path[namelen] == '/'))
                        break;
 
                debug_pop(elem);
-               attr_stack = elem->prev;
-               free_attr_elem(elem);
+               *stack = elem->prev;
+               attr_stack_free(elem);
        }
 
        /*
-        * Read from parent directories and push them down
+        * bootstrap_attr_stack() should have added, and the
+        * above loop should have stopped before popping, the
+        * root element whose attr_stack->origin is set to an
+        * empty string.
         */
-       if (!is_bare_repository() || direction == GIT_ATTR_INDEX) {
-               /*
-                * bootstrap_attr_stack() should have added, and the
-                * above loop should have stopped before popping, the
-                * root element whose attr_stack->origin is set to an
-                * empty string.
-                */
-               struct strbuf pathbuf = STRBUF_INIT;
-
-               assert(attr_stack->origin);
-               while (1) {
-                       len = strlen(attr_stack->origin);
-                       if (dirlen <= len)
-                               break;
-                       cp = memchr(path + len + 1, '/', dirlen - len - 1);
-                       if (!cp)
-                               cp = path + dirlen;
-                       strbuf_add(&pathbuf, path, cp - path);
+       assert((*stack)->origin);
+
+       strbuf_addstr(&pathbuf, (*stack)->origin);
+       /* Build up to the directory 'path' is in */
+       while (pathbuf.len < dirlen) {
+               size_t len = pathbuf.len;
+               struct attr_stack *next;
+               char *origin;
+
+               /* Skip path-separator */
+               if (len < dirlen && is_dir_sep(path[len]))
+                       len++;
+               /* Find the end of the next component */
+               while (len < dirlen && !is_dir_sep(path[len]))
+                       len++;
+
+               if (pathbuf.len > 0)
                        strbuf_addch(&pathbuf, '/');
-                       strbuf_addstr(&pathbuf, GITATTRIBUTES_FILE);
-                       elem = read_attr(pathbuf.buf, 0);
-                       strbuf_setlen(&pathbuf, cp - path);
-                       elem->origin = strbuf_detach(&pathbuf, &elem->originlen);
-                       elem->prev = attr_stack;
-                       attr_stack = elem;
-                       debug_push(elem);
-               }
+               strbuf_add(&pathbuf, path + pathbuf.len, (len - pathbuf.len));
+               strbuf_addf(&pathbuf, "/%s", GITATTRIBUTES_FILE);
+
+               next = read_attr(pathbuf.buf, 0);
+
+               /* reset the pathbuf to not include "/.gitattributes" */
+               strbuf_setlen(&pathbuf, len);
 
-               strbuf_release(&pathbuf);
+               origin = xstrdup(pathbuf.buf);
+               push_stack(stack, next, origin, len);
        }
 
        /*
         * Finally push the "info" one at the top of the stack.
         */
-       info->prev = attr_stack;
-       attr_stack = info;
+       push_stack(stack, info, NULL, 0);
+
+       strbuf_release(&pathbuf);
 }
 
 static int path_matches(const char *pathname, int pathlen,
@@ -656,16 +1011,16 @@ static int path_matches(const char *pathname, int pathlen,
                              pattern, prefix, pat->patternlen, pat->flags);
 }
 
-static int macroexpand_one(int attr_nr, int rem);
+static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem);
 
-static int fill_one(const char *what, struct match_attr *a, int rem)
+static int fill_one(const char *what, struct all_attrs_item *all_attrs,
+                   const struct match_attr *a, int rem)
 {
-       struct git_attr_check *check = check_all_attr;
        int i;
 
-       for (i = a->num_attr - 1; 0 < rem && 0 <= i; i--) {
-               struct git_attr *attr = a->state[i].attr;
-               const char **n = &(check[attr->attr_nr].value);
+       for (i = a->num_attr - 1; rem > 0 && i >= 0; i--) {
+               const struct git_attr *attr = a->state[i].attr;
+               const char **n = &(all_attrs[attr->attr_nr].value);
                const char *v = a->state[i].setto;
 
                if (*n == ATTR__UNKNOWN) {
@@ -674,64 +1029,72 @@ static int fill_one(const char *what, struct match_attr *a, int rem)
                                  attr, v);
                        *n = v;
                        rem--;
-                       rem = macroexpand_one(attr->attr_nr, rem);
+                       rem = macroexpand_one(all_attrs, attr->attr_nr, rem);
                }
        }
        return rem;
 }
 
 static int fill(const char *path, int pathlen, int basename_offset,
-               struct attr_stack *stk, int rem)
+               const struct attr_stack *stack,
+               struct all_attrs_item *all_attrs, int rem)
 {
-       int i;
-       const char *base = stk->origin ? stk->origin : "";
+       for (; rem > 0 && stack; stack = stack->prev) {
+               int i;
+               const char *base = stack->origin ? stack->origin : "";
 
-       for (i = stk->num_matches - 1; 0 < rem && 0 <= i; i--) {
-               struct match_attr *a = stk->attrs[i];
-               if (a->is_macro)
-                       continue;
-               if (path_matches(path, pathlen, basename_offset,
-                                &a->u.pat, base, stk->originlen))
-                       rem = fill_one("fill", a, rem);
+               for (i = stack->num_matches - 1; 0 < rem && 0 <= i; i--) {
+                       const struct match_attr *a = stack->attrs[i];
+                       if (a->is_macro)
+                               continue;
+                       if (path_matches(path, pathlen, basename_offset,
+                                        &a->u.pat, base, stack->originlen))
+                               rem = fill_one("fill", all_attrs, a, rem);
+               }
        }
+
        return rem;
 }
 
-static int macroexpand_one(int nr, int rem)
+static int macroexpand_one(struct all_attrs_item *all_attrs, int nr, int rem)
 {
-       struct attr_stack *stk;
-       struct match_attr *a = NULL;
-       int i;
+       const struct all_attrs_item *item = &all_attrs[nr];
 
-       if (check_all_attr[nr].value != ATTR__TRUE ||
-           !check_all_attr[nr].attr->maybe_macro)
+       if (item->macro && item->value == ATTR__TRUE)
+               return fill_one("expand", all_attrs, item->macro, rem);
+       else
                return rem;
+}
 
-       for (stk = attr_stack; !a && stk; stk = stk->prev)
-               for (i = stk->num_matches - 1; !a && 0 <= i; i--) {
-                       struct match_attr *ma = stk->attrs[i];
-                       if (!ma->is_macro)
-                               continue;
-                       if (ma->u.attr->attr_nr == nr)
-                               a = ma;
+/*
+ * Marks the attributes which are macros based on the attribute stack.
+ * This prevents having to search through the attribute stack each time
+ * a macro needs to be expanded during the fill stage.
+ */
+static void determine_macros(struct all_attrs_item *all_attrs,
+                            const struct attr_stack *stack)
+{
+       for (; stack; stack = stack->prev) {
+               int i;
+               for (i = stack->num_matches - 1; i >= 0; i--) {
+                       const struct match_attr *ma = stack->attrs[i];
+                       if (ma->is_macro) {
+                               int n = ma->u.attr->attr_nr;
+                               if (!all_attrs[n].macro) {
+                                       all_attrs[n].macro = ma;
+                               }
+                       }
                }
-
-       if (a)
-               rem = fill_one("expand", a, rem);
-
-       return rem;
+       }
 }
 
 /*
- * Collect attributes for path into the array pointed to by
- * check_all_attr. If num is non-zero, only attributes in check[] are
- * collected. Otherwise all attributes are collected.
+ * Collect attributes for path into the array pointed to by check->all_attrs.
+ * If check->check_nr is non-zero, only attributes in check[] are collected.
+ * Otherwise all attributes are collected.
  */
-static void collect_some_attrs(const char *path, int num,
-                              struct git_attr_check *check)
-
+static void collect_some_attrs(const char *path, struct attr_check *check)
 {
-       struct attr_stack *stk;
        int i, pathlen, rem, dirlen;
        const char *cp, *last_slash = NULL;
        int basename_offset;
@@ -749,81 +1112,67 @@ static void collect_some_attrs(const char *path, int num,
                dirlen = 0;
        }
 
-       prepare_attr_stack(path, dirlen);
-       for (i = 0; i < attr_nr; i++)
-               check_all_attr[i].value = ATTR__UNKNOWN;
-       if (num && !cannot_trust_maybe_real) {
+       prepare_attr_stack(path, dirlen, &check->stack);
+       all_attrs_init(&g_attr_hashmap, check);
+       determine_macros(check->all_attrs, check->stack);
+
+       if (check->nr) {
                rem = 0;
-               for (i = 0; i < num; i++) {
-                       if (!check[i].attr->maybe_real) {
-                               struct git_attr_check *c;
-                               c = check_all_attr + check[i].attr->attr_nr;
-                               c->value = ATTR__UNSET;
+               for (i = 0; i < check->nr; i++) {
+                       int n = check->items[i].attr->attr_nr;
+                       struct all_attrs_item *item = &check->all_attrs[n];
+                       if (item->macro) {
+                               item->value = ATTR__UNSET;
                                rem++;
                        }
                }
-               if (rem == num)
+               if (rem == check->nr)
                        return;
        }
 
-       rem = attr_nr;
-       for (stk = attr_stack; 0 < rem && stk; stk = stk->prev)
-               rem = fill(path, pathlen, basename_offset, stk, rem);
+       rem = check->all_attrs_nr;
+       fill(path, pathlen, basename_offset, check->stack, check->all_attrs, rem);
 }
 
-int git_check_attr(const char *path, int num, struct git_attr_check *check)
+int git_check_attr(const char *path, struct attr_check *check)
 {
        int i;
 
-       collect_some_attrs(path, num, check);
+       collect_some_attrs(path, check);
 
-       for (i = 0; i < num; i++) {
-               const char *value = check_all_attr[check[i].attr->attr_nr].value;
+       for (i = 0; i < check->nr; i++) {
+               size_t n = check->items[i].attr->attr_nr;
+               const char *value = check->all_attrs[n].value;
                if (value == ATTR__UNKNOWN)
                        value = ATTR__UNSET;
-               check[i].value = value;
+               check->items[i].value = value;
        }
 
        return 0;
 }
 
-int git_all_attrs(const char *path, int *num, struct git_attr_check **check)
+void git_all_attrs(const char *path, struct attr_check *check)
 {
-       int i, count, j;
+       int i;
 
-       collect_some_attrs(path, 0, NULL);
+       attr_check_reset(check);
+       collect_some_attrs(path, check);
 
-       /* Count the number of attributes that are set. */
-       count = 0;
-       for (i = 0; i < attr_nr; i++) {
-               const char *value = check_all_attr[i].value;
-               if (value != ATTR__UNSET && value != ATTR__UNKNOWN)
-                       ++count;
-       }
-       *num = count;
-       ALLOC_ARRAY(*check, count);
-       j = 0;
-       for (i = 0; i < attr_nr; i++) {
-               const char *value = check_all_attr[i].value;
-               if (value != ATTR__UNSET && value != ATTR__UNKNOWN) {
-                       (*check)[j].attr = check_all_attr[i].attr;
-                       (*check)[j].value = value;
-                       ++j;
-               }
+       for (i = 0; i < check->all_attrs_nr; i++) {
+               const char *name = check->all_attrs[i].attr->name;
+               const char *value = check->all_attrs[i].value;
+               struct attr_check_item *item;
+               if (value == ATTR__UNSET || value == ATTR__UNKNOWN)
+                       continue;
+               item = attr_check_append(check, git_attr(name));
+               item->value = value;
        }
-
-       return 0;
 }
 
-void git_attr_set_direction(enum git_attr_direction new, struct index_state *istate)
+void attr_start(void)
 {
-       enum git_attr_direction old = direction;
-
-       if (is_bare_repository() && new != GIT_ATTR_INDEX)
-               die("BUG: non-INDEX attr direction in a bare repo");
-
-       direction = new;
-       if (new != old)
-               drop_attr_stack();
-       use_index = istate;
+#ifndef NO_PTHREADS
+       pthread_mutex_init(&g_attr_hashmap.mutex, NULL);
+       pthread_mutex_init(&check_vector.mutex, NULL);
+#endif
 }
diff --git a/attr.h b/attr.h
index 8b08d33af84ebbb376a69d85f3db6c03eeb78a63..442d464db6271da48a4ef133127c7d3a18c186cf 100644 (file)
--- a/attr.h
+++ b/attr.h
@@ -4,11 +4,15 @@
 /* An attribute is a pointer to this opaque structure */
 struct git_attr;
 
+/* opaque structures used internally for attribute collection */
+struct all_attrs_item;
+struct attr_stack;
+
 /*
  * Given a string, return the gitattribute object that
  * corresponds to it.
  */
-struct git_attr *git_attr(const char *);
+const struct git_attr *git_attr(const char *);
 
 /* Internal use */
 extern const char git_attr__true[];
@@ -20,38 +24,58 @@ extern const char git_attr__false[];
 #define ATTR_UNSET(v) ((v) == NULL)
 
 /*
- * Send one or more git_attr_check to git_check_attr(), and
+ * Send one or more git_attr_check to git_check_attrs(), and
  * each 'value' member tells what its value is.
  * Unset one is returned as NULL.
  */
-struct git_attr_check {
-       struct git_attr *attr;
+struct attr_check_item {
+       const struct git_attr *attr;
        const char *value;
 };
 
+struct attr_check {
+       int nr;
+       int alloc;
+       struct attr_check_item *items;
+       int all_attrs_nr;
+       struct all_attrs_item *all_attrs;
+       struct attr_stack *stack;
+};
+
+extern struct attr_check *attr_check_alloc(void);
+extern struct attr_check *attr_check_initl(const char *, ...);
+extern struct attr_check *attr_check_dup(const struct attr_check *check);
+
+extern struct attr_check_item *attr_check_append(struct attr_check *check,
+                                                const struct git_attr *attr);
+
+extern void attr_check_reset(struct attr_check *check);
+extern void attr_check_clear(struct attr_check *check);
+extern void attr_check_free(struct attr_check *check);
+
 /*
  * Return the name of the attribute represented by the argument.  The
  * return value is a pointer to a null-delimited string that is part
  * of the internal data structure; it should not be modified or freed.
  */
-char *git_attr_name(struct git_attr *);
+extern const char *git_attr_name(const struct git_attr *);
 
-int git_check_attr(const char *path, int, struct git_attr_check *);
+extern int git_check_attr(const char *path, struct attr_check *check);
 
 /*
- * Retrieve all attributes that apply to the specified path.  *num
- * will be set to the number of attributes on the path; **check will
- * be set to point at a newly-allocated array of git_attr_check
- * objects describing the attributes and their values.  *check must be
- * free()ed by the caller.
+ * Retrieve all attributes that apply to the specified path.
+ * check holds the attributes and their values.
  */
-int git_all_attrs(const char *path, int *num, struct git_attr_check **check);
+extern void git_all_attrs(const char *path, struct attr_check *check);
 
 enum git_attr_direction {
        GIT_ATTR_CHECKIN,
        GIT_ATTR_CHECKOUT,
        GIT_ATTR_INDEX
 };
-void git_attr_set_direction(enum git_attr_direction, struct index_state *);
+void git_attr_set_direction(enum git_attr_direction new_direction,
+                           struct index_state *istate);
+
+extern void attr_start(void);
 
 #endif /* ATTR_H */
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 b955d4f316c799f7788896806302bfd4dbcf3ec4..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)
@@ -345,7 +345,8 @@ void die_if_checked_out(const char *branch, int ignore_current_worktree)
            branch, wt->path);
 }
 
-int replace_each_worktree_head_symref(const char *oldref, const char *newref)
+int replace_each_worktree_head_symref(const char *oldref, const char *newref,
+                                     const char *logmsg)
 {
        int ret = 0;
        struct worktree **worktrees = get_worktrees(0);
@@ -358,7 +359,7 @@ int replace_each_worktree_head_symref(const char *oldref, const char *newref)
                        continue;
 
                if (set_worktree_head_symref(get_worktree_git_dir(worktrees[i]),
-                                            newref)) {
+                                            newref, logmsg)) {
                        ret = -1;
                        error(_("HEAD of working tree %s is not updated"),
                              worktrees[i]->path);
index 3103eb9add31e08600f9fa158303417e36d3b229..b07788558c0e76a701b571f73c0462dcf2abcc63 100644 (file)
--- a/branch.h
+++ b/branch.h
@@ -71,6 +71,7 @@ extern void die_if_checked_out(const char *branch, int ignore_current_worktree);
  * This will be used when renaming a branch. Returns 0 if successful, non-zero
  * otherwise.
  */
-extern int replace_each_worktree_head_symref(const char *oldref, const char *newref);
+extern int replace_each_worktree_head_symref(const char *oldref, const char *newref,
+                                            const char *logmsg);
 
 #endif
index 67f80519dafc4875434437a34e438453bc2c78c4..9e4a89816d5fbde0ed0b3bebff8f90f70dd4480d 100644 (file)
--- a/builtin.h
+++ b/builtin.h
@@ -103,6 +103,7 @@ extern int cmd_prune_packed(int argc, const char **argv, const char *prefix);
 extern int cmd_pull(int argc, const char **argv, const char *prefix);
 extern int cmd_push(int argc, const char **argv, const char *prefix);
 extern int cmd_read_tree(int argc, const char **argv, const char *prefix);
+extern int cmd_rebase__helper(int argc, const char **argv, const char *prefix);
 extern int cmd_receive_pack(int argc, const char **argv, const char *prefix);
 extern int cmd_reflog(int argc, const char **argv, const char *prefix);
 extern int cmd_remote(int argc, const char **argv, const char *prefix);
index 31fb60578f6caacfb376fe84938dda9432dcfc5a..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);
@@ -1049,7 +1053,7 @@ static void am_setup(struct am_state *state, enum patch_format patch_format,
        } else {
                write_state_text(state, "abort-safety", "");
                if (!state->rebasing)
-                       delete_ref("ORIG_HEAD", NULL, 0);
+                       delete_ref(NULL, "ORIG_HEAD", NULL, 0);
        }
 
        /*
@@ -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);
                }
@@ -2172,7 +2173,7 @@ static void am_abort(struct am_state *state)
                                has_curr_head ? &curr_head : NULL, 0,
                                UPDATE_REFS_DIE_ON_ERR);
        else if (curr_branch)
-               delete_ref(curr_branch, NULL, REF_NODEREF);
+               delete_ref(NULL, curr_branch, NULL, REF_NODEREF);
 
        free(curr_branch);
        am_destroy(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 babfd0f73063060eea595fccabefe55f9f631d26..0552c42ad115bba218f35d4836f1021e3385f6c2 100644 (file)
@@ -28,20 +28,21 @@ static const char * const builtin_branch_usage[] = {
        N_("git branch [<options>] [-r] (-d | -D) <branch-name>..."),
        N_("git branch [<options>] (-m | -M) [<old-branch>] <new-branch>"),
        N_("git branch [<options>] [-r | -a] [--points-at]"),
+       N_("git branch [<options>] [-r | -a] [--format]"),
        NULL
 };
 
 static const char *head;
-static unsigned char head_sha1[20];
+static struct object_id head_oid;
 
 static int branch_use_color = -1;
 static char branch_colors[][COLOR_MAXLEN] = {
        GIT_COLOR_RESET,
-       GIT_COLOR_NORMAL,       /* PLAIN */
-       GIT_COLOR_RED,          /* REMOTE */
-       GIT_COLOR_NORMAL,       /* LOCAL */
-       GIT_COLOR_GREEN,        /* CURRENT */
-       GIT_COLOR_BLUE,         /* UPSTREAM */
+       GIT_COLOR_NORMAL,       /* PLAIN */
+       GIT_COLOR_RED,          /* REMOTE */
+       GIT_COLOR_NORMAL,       /* LOCAL */
+       GIT_COLOR_GREEN,        /* CURRENT */
+       GIT_COLOR_BLUE,         /* UPSTREAM */
 };
 enum color_branch {
        BRANCH_COLOR_RESET = 0,
@@ -117,13 +118,13 @@ static int branch_merged(int kind, const char *name,
        if (kind == FILTER_REFS_BRANCHES) {
                struct branch *branch = branch_get(name);
                const char *upstream = branch_get_upstream(branch, NULL);
-               unsigned char sha1[20];
+               struct object_id oid;
 
                if (upstream &&
                    (reference_name = reference_name_to_free =
                     resolve_refdup(upstream, RESOLVE_REF_READING,
-                                   sha1, NULL)) != NULL)
-                       reference_rev = lookup_commit_reference(sha1);
+                                   oid.hash, NULL)) != NULL)
+                       reference_rev = lookup_commit_reference(oid.hash);
        }
        if (!reference_rev)
                reference_rev = head_rev;
@@ -153,10 +154,10 @@ static int branch_merged(int kind, const char *name,
 }
 
 static int check_branch_commit(const char *branchname, const char *refname,
-                              const unsigned char *sha1, struct commit *head_rev,
+                              const struct object_id *oid, struct commit *head_rev,
                               int kinds, int force)
 {
-       struct commit *rev = lookup_commit_reference(sha1);
+       struct commit *rev = lookup_commit_reference(oid->hash);
        if (!rev) {
                error(_("Couldn't look up commit object for '%s'"), refname);
                return -1;
@@ -183,7 +184,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                           int quiet)
 {
        struct commit *head_rev = NULL;
-       unsigned char sha1[20];
+       struct object_id oid;
        char *name = NULL;
        const char *fmt;
        int i;
@@ -210,7 +211,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
        }
 
        if (!force) {
-               head_rev = lookup_commit_reference(head_sha1);
+               head_rev = lookup_commit_reference(head_oid.hash);
                if (!head_rev)
                        die(_("Couldn't look up commit object for HEAD"));
        }
@@ -238,7 +239,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                                        RESOLVE_REF_READING
                                        | RESOLVE_REF_NO_RECURSE
                                        | RESOLVE_REF_ALLOW_BAD_NAME,
-                                       sha1, &flags);
+                                       oid.hash, &flags);
                if (!target) {
                        error(remote_branch
                              ? _("remote-tracking branch '%s' not found.")
@@ -248,13 +249,13 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                }
 
                if (!(flags & (REF_ISSYMREF|REF_ISBROKEN)) &&
-                   check_branch_commit(bname.buf, name, sha1, head_rev, kinds,
+                   check_branch_commit(bname.buf, name, &oid, head_rev, kinds,
                                        force)) {
                        ret = 1;
                        goto next;
                }
 
-               if (delete_ref(name, is_null_sha1(sha1) ? NULL : sha1,
+               if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : oid.hash,
                               REF_NODEREF)) {
                        error(remote_branch
                              ? _("Error deleting remote-tracking branch '%s'")
@@ -270,7 +271,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                               bname.buf,
                               (flags & REF_ISBROKEN) ? "broken"
                               : (flags & REF_ISSYMREF) ? target
-                              : find_unique_abbrev(sha1, DEFAULT_ABBREV));
+                              : find_unique_abbrev(oid.hash, DEFAULT_ABBREV));
                }
                delete_branch_config(bname.buf);
 
@@ -283,221 +284,109 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
        return(ret);
 }
 
-static void fill_tracking_info(struct strbuf *stat, const char *branch_name,
-               int show_upstream_ref)
+static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
 {
-       int ours, theirs;
-       char *ref = NULL;
-       struct branch *branch = branch_get(branch_name);
-       const char *upstream;
-       struct strbuf fancy = STRBUF_INIT;
-       int upstream_is_gone = 0;
-       int added_decoration = 1;
-
-       if (stat_tracking_info(branch, &ours, &theirs, &upstream) < 0) {
-               if (!upstream)
-                       return;
-               upstream_is_gone = 1;
-       }
-
-       if (show_upstream_ref) {
-               ref = shorten_unambiguous_ref(upstream, 0);
-               if (want_color(branch_use_color))
-                       strbuf_addf(&fancy, "%s%s%s",
-                                       branch_get_color(BRANCH_COLOR_UPSTREAM),
-                                       ref, branch_get_color(BRANCH_COLOR_RESET));
-               else
-                       strbuf_addstr(&fancy, ref);
-       }
+       int i, max = 0;
+       for (i = 0; i < refs->nr; i++) {
+               struct ref_array_item *it = refs->items[i];
+               const char *desc = it->refname;
+               int w;
 
-       if (upstream_is_gone) {
-               if (show_upstream_ref)
-                       strbuf_addf(stat, _("[%s: gone]"), fancy.buf);
-               else
-                       added_decoration = 0;
-       } else if (!ours && !theirs) {
-               if (show_upstream_ref)
-                       strbuf_addf(stat, _("[%s]"), fancy.buf);
-               else
-                       added_decoration = 0;
-       } else if (!ours) {
-               if (show_upstream_ref)
-                       strbuf_addf(stat, _("[%s: behind %d]"), fancy.buf, theirs);
-               else
-                       strbuf_addf(stat, _("[behind %d]"), theirs);
+               skip_prefix(it->refname, "refs/heads/", &desc);
+               skip_prefix(it->refname, "refs/remotes/", &desc);
+               if (it->kind == FILTER_REFS_DETACHED_HEAD) {
+                       char *head_desc = get_head_description();
+                       w = utf8_strwidth(head_desc);
+                       free(head_desc);
+               } else
+                       w = utf8_strwidth(desc);
 
-       } else if (!theirs) {
-               if (show_upstream_ref)
-                       strbuf_addf(stat, _("[%s: ahead %d]"), fancy.buf, ours);
-               else
-                       strbuf_addf(stat, _("[ahead %d]"), ours);
-       } else {
-               if (show_upstream_ref)
-                       strbuf_addf(stat, _("[%s: ahead %d, behind %d]"),
-                                   fancy.buf, ours, theirs);
-               else
-                       strbuf_addf(stat, _("[ahead %d, behind %d]"),
-                                   ours, theirs);
+               if (it->kind == FILTER_REFS_REMOTES)
+                       w += remote_bonus;
+               if (w > max)
+                       max = w;
        }
-       strbuf_release(&fancy);
-       if (added_decoration)
-               strbuf_addch(stat, ' ');
-       free(ref);
+       return max;
 }
 
-static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
-                            struct ref_filter *filter, const char *refname)
+static const char *quote_literal_for_format(const char *s)
 {
-       struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT;
-       const char *sub = _(" **** invalid ref ****");
-       struct commit *commit = item->commit;
+       static struct strbuf buf = STRBUF_INIT;
 
-       if (!parse_commit(commit)) {
-               pp_commit_easy(CMIT_FMT_ONELINE, commit, &subject);
-               sub = subject.buf;
+       strbuf_reset(&buf);
+       while (*s) {
+               const char *ep = strchrnul(s, '%');
+               if (s < ep)
+                       strbuf_add(&buf, s, ep - s);
+               if (*ep == '%') {
+                       strbuf_addstr(&buf, "%%");
+                       s = ep + 1;
+               } else {
+                       s = ep;
+               }
        }
-
-       if (item->kind == FILTER_REFS_BRANCHES)
-               fill_tracking_info(&stat, refname, filter->verbose > 1);
-
-       strbuf_addf(out, " %s %s%s",
-               find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),
-               stat.buf, sub);
-       strbuf_release(&stat);
-       strbuf_release(&subject);
+       return buf.buf;
 }
 
-static char *get_head_description(void)
+static char *build_format(struct ref_filter *filter, int maxwidth, const char *remote_prefix)
 {
-       struct strbuf desc = STRBUF_INIT;
-       struct wt_status_state state;
-       memset(&state, 0, sizeof(state));
-       wt_status_get_state(&state, 1);
-       if (state.rebase_in_progress ||
-           state.rebase_interactive_in_progress)
-               strbuf_addf(&desc, _("(no branch, rebasing %s)"),
-                           state.branch);
-       else if (state.bisect_in_progress)
-               strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
-                           state.branch);
-       else if (state.detached_from) {
-               if (state.detached_at)
-                       /* TRANSLATORS: make sure this matches
-                          "HEAD detached at " in wt-status.c */
-                       strbuf_addf(&desc, _("(HEAD detached at %s)"),
-                               state.detached_from);
-               else
-                       /* TRANSLATORS: make sure this matches
-                          "HEAD detached from " in wt-status.c */
-                       strbuf_addf(&desc, _("(HEAD detached from %s)"),
-                               state.detached_from);
-       }
-       else
-               strbuf_addstr(&desc, _("(no branch)"));
-       free(state.branch);
-       free(state.onto);
-       free(state.detached_from);
-       return strbuf_detach(&desc, NULL);
-}
+       struct strbuf fmt = STRBUF_INIT;
+       struct strbuf local = STRBUF_INIT;
+       struct strbuf remote = STRBUF_INIT;
 
-static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth,
-                                     struct ref_filter *filter, const char *remote_prefix)
-{
-       char c;
-       int current = 0;
-       int color;
-       struct strbuf out = STRBUF_INIT, name = STRBUF_INIT;
-       const char *prefix_to_show = "";
-       const char *prefix_to_skip = NULL;
-       const char *desc = item->refname;
-       char *to_free = NULL;
+       strbuf_addf(&fmt, "%%(if)%%(HEAD)%%(then)* %s%%(else)  %%(end)",
+                   branch_get_color(BRANCH_COLOR_CURRENT));
 
-       switch (item->kind) {
-       case FILTER_REFS_BRANCHES:
-               prefix_to_skip = "refs/heads/";
-               skip_prefix(desc, prefix_to_skip, &desc);
-               if (!filter->detached && !strcmp(desc, head))
-                       current = 1;
+       if (filter->verbose) {
+               struct strbuf obname = STRBUF_INIT;
+
+               if (filter->abbrev < 0)
+                       strbuf_addf(&obname, "%%(objectname:short)");
+               else if (!filter->abbrev)
+                       strbuf_addf(&obname, "%%(objectname)");
                else
-                       color = BRANCH_COLOR_LOCAL;
-               break;
-       case FILTER_REFS_REMOTES:
-               prefix_to_skip = "refs/remotes/";
-               skip_prefix(desc, prefix_to_skip, &desc);
-               color = BRANCH_COLOR_REMOTE;
-               prefix_to_show = remote_prefix;
-               break;
-       case FILTER_REFS_DETACHED_HEAD:
-               desc = to_free = get_head_description();
-               current = 1;
-               break;
-       default:
-               color = BRANCH_COLOR_PLAIN;
-               break;
-       }
+                       strbuf_addf(&obname, "%%(objectname:short=%d)", filter->abbrev);
 
-       c = ' ';
-       if (current) {
-               c = '*';
-               color = BRANCH_COLOR_CURRENT;
-       }
+               strbuf_addf(&local, "%%(align:%d,left)%%(refname:lstrip=2)%%(end)", maxwidth);
+               strbuf_addf(&local, "%s", branch_get_color(BRANCH_COLOR_RESET));
+               strbuf_addf(&local, " %s ", obname.buf);
 
-       strbuf_addf(&name, "%s%s", prefix_to_show, desc);
-       if (filter->verbose) {
-               int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf);
-               strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color),
-                           maxwidth + utf8_compensation, name.buf,
-                           branch_get_color(BRANCH_COLOR_RESET));
-       } else
-               strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color),
-                           name.buf, branch_get_color(BRANCH_COLOR_RESET));
-
-       if (item->symref) {
-               const char *symref = item->symref;
-               if (prefix_to_skip)
-                       skip_prefix(symref, prefix_to_skip, &symref);
-               strbuf_addf(&out, " -> %s", symref);
-       }
-       else if (filter->verbose)
-               /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */
-               add_verbose_info(&out, item, filter, desc);
-       if (column_active(colopts)) {
-               assert(!filter->verbose && "--column and --verbose are incompatible");
-               string_list_append(&output, out.buf);
+               if (filter->verbose > 1)
+                       strbuf_addf(&local, "%%(if)%%(upstream)%%(then)[%s%%(upstream:short)%s%%(if)%%(upstream:track)"
+                                   "%%(then): %%(upstream:track,nobracket)%%(end)] %%(end)%%(contents:subject)",
+                                   branch_get_color(BRANCH_COLOR_UPSTREAM), branch_get_color(BRANCH_COLOR_RESET));
+               else
+                       strbuf_addf(&local, "%%(if)%%(upstream:track)%%(then)%%(upstream:track) %%(end)%%(contents:subject)");
+
+               strbuf_addf(&remote, "%s%%(align:%d,left)%s%%(refname:lstrip=2)%%(end)%s"
+                           "%%(if)%%(symref)%%(then) -> %%(symref:short)"
+                           "%%(else) %s %%(contents:subject)%%(end)",
+                           branch_get_color(BRANCH_COLOR_REMOTE), maxwidth, quote_literal_for_format(remote_prefix),
+                           branch_get_color(BRANCH_COLOR_RESET), obname.buf);
+               strbuf_release(&obname);
        } else {
-               printf("%s\n", out.buf);
+               strbuf_addf(&local, "%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
+                           branch_get_color(BRANCH_COLOR_RESET));
+               strbuf_addf(&remote, "%s%s%%(refname:lstrip=2)%s%%(if)%%(symref)%%(then) -> %%(symref:short)%%(end)",
+                           branch_get_color(BRANCH_COLOR_REMOTE), quote_literal_for_format(remote_prefix),
+                           branch_get_color(BRANCH_COLOR_RESET));
        }
-       strbuf_release(&name);
-       strbuf_release(&out);
-       free(to_free);
-}
 
-static int calc_maxwidth(struct ref_array *refs, int remote_bonus)
-{
-       int i, max = 0;
-       for (i = 0; i < refs->nr; i++) {
-               struct ref_array_item *it = refs->items[i];
-               const char *desc = it->refname;
-               int w;
+       strbuf_addf(&fmt, "%%(if:notequals=refs/remotes)%%(refname:rstrip=-2)%%(then)%s%%(else)%s%%(end)", local.buf, remote.buf);
 
-               skip_prefix(it->refname, "refs/heads/", &desc);
-               skip_prefix(it->refname, "refs/remotes/", &desc);
-               w = utf8_strwidth(desc);
-
-               if (it->kind == FILTER_REFS_REMOTES)
-                       w += remote_bonus;
-               if (w > max)
-                       max = w;
-       }
-       return max;
+       strbuf_release(&local);
+       strbuf_release(&remote);
+       return strbuf_detach(&fmt, NULL);
 }
 
-static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting)
+static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
 {
        int i;
        struct ref_array array;
        int maxwidth = 0;
        const char *remote_prefix = "";
+       struct strbuf out = STRBUF_INIT;
+       char *to_free = NULL;
 
        /*
         * If we are listing more than just remote branches,
@@ -509,18 +398,32 @@ static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sortin
 
        memset(&array, 0, sizeof(array));
 
-       verify_ref_format("%(refname)%(symref)");
        filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN);
 
        if (filter->verbose)
                maxwidth = calc_maxwidth(&array, strlen(remote_prefix));
 
+       if (!format)
+               format = to_free = build_format(filter, maxwidth, remote_prefix);
+       verify_ref_format(format);
+
        ref_array_sort(sorting, &array);
 
-       for (i = 0; i < array.nr; i++)
-               format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix);
+       for (i = 0; i < array.nr; i++) {
+               format_ref_array_item(array.items[i], format, 0, &out);
+               if (column_active(colopts)) {
+                       assert(!filter->verbose && "--column and --verbose are incompatible");
+                        /* format to a string_list to let print_columns() do its job */
+                       string_list_append(&output, out.buf);
+               } else {
+                       fwrite(out.buf, 1, out.len, stdout);
+                       putchar('\n');
+               }
+               strbuf_release(&out);
+       }
 
        ref_array_clear(&array);
+       free(to_free);
 }
 
 static void reject_rebase_or_bisect_branch(const char *target)
@@ -582,14 +485,15 @@ static void rename_branch(const char *oldname, const char *newname, int force)
 
        if (rename_ref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch rename failed"));
-       strbuf_release(&logmsg);
 
        if (recovery)
                warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
 
-       if (replace_each_worktree_head_symref(oldref.buf, newref.buf))
+       if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
                die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
 
+       strbuf_release(&logmsg);
+
        strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);
        strbuf_release(&oldref);
        strbuf_addf(&newsection, "branch.%s", newref.buf + 11);
@@ -641,6 +545,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
        struct ref_filter filter;
        int icase = 0;
        static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+       const char *format = NULL;
 
        struct option options[] = {
                OPT_GROUP(N_("Generic options")),
@@ -657,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:")),
@@ -682,9 +589,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                        N_("print only branches of the object"), 0, parse_opt_object_name
                },
                OPT_BOOL('i', "ignore-case", &icase, N_("sorting and filtering are case insensitive")),
+               OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
                OPT_END(),
        };
 
+       setup_ref_filter_porcelain_msg();
+
        memset(&filter, 0, sizeof(filter));
        filter.kind = FILTER_REFS_BRANCHES;
        filter.abbrev = -1;
@@ -696,7 +606,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
 
        track = git_branch_track;
 
-       head = resolve_refdup("HEAD", 0, head_sha1, NULL);
+       head = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
        if (!head)
                die(_("Failed to resolve HEAD as a valid ref."));
        if (!strcmp(head, "HEAD"))
@@ -710,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 +
@@ -752,7 +663,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                if (!sorting)
                        sorting = ref_default_sorting();
                sorting->ignore_case = icase;
-               print_ref_list(&filter, sorting);
+               print_ref_list(&filter, sorting, format);
                print_columns(&output, colopts, NULL);
                string_list_clear(&output, 0);
                return 0;
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 30383e9eb4befb31e0d165d5863615484bcc9da3..1890d7a6390dcae719af33ce5cb59e74832a80e8 100644 (file)
@@ -401,28 +401,28 @@ 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;
 }
 
-static int batch_loose_object(const unsigned char *sha1,
+static int batch_loose_object(const struct object_id *oid,
                              const char *path,
                              void *data)
 {
-       sha1_array_append(data, sha1);
+       oid_array_append(data, oid);
        return 0;
 }
 
-static int batch_packed_object(const unsigned char *sha1,
+static int batch_packed_object(const struct object_id *oid,
                               struct packed_git *pack,
                               uint32_t pos,
                               void *data)
 {
-       sha1_array_append(data, sha1);
+       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 53a5a18c1681f5f13f6ddd4fd4d7d30c0ea14ce6..4d01ca0c8ba0ba2a1d2c209b766e19d9ca9c8ac0 100644 (file)
@@ -24,12 +24,13 @@ static const struct option check_attr_options[] = {
        OPT_END()
 };
 
-static void output_attr(int cnt, struct git_attr_check *check,
-       const char *file)
+static void output_attr(struct attr_check *check, const char *file)
 {
        int j;
+       int cnt = check->nr;
+
        for (j = 0; j < cnt; j++) {
-               const char *value = check[j].value;
+               const char *value = check->items[j].value;
 
                if (ATTR_TRUE(value))
                        value = "set";
@@ -42,35 +43,38 @@ static void output_attr(int cnt, struct git_attr_check *check,
                        printf("%s%c" /* path */
                               "%s%c" /* attrname */
                               "%s%c" /* attrvalue */,
-                              file, 0, git_attr_name(check[j].attr), 0, value, 0);
+                              file, 0,
+                              git_attr_name(check->items[j].attr), 0, value, 0);
                } else {
                        quote_c_style(file, NULL, stdout, 0);
-                       printf(": %s: %s\n", git_attr_name(check[j].attr), value);
+                       printf(": %s: %s\n",
+                              git_attr_name(check->items[j].attr), value);
                }
-
        }
 }
 
-static void check_attr(const char *prefix, int cnt,
-       struct git_attr_check *check, const char *file)
+static void check_attr(const char *prefix,
+                      struct attr_check *check,
+                      int collect_all,
+                      const char *file)
 {
        char *full_path =
                prefix_path(prefix, prefix ? strlen(prefix) : 0, file);
-       if (check != NULL) {
-               if (git_check_attr(full_path, cnt, check))
-                       die("git_check_attr died");
-               output_attr(cnt, check, file);
+
+       if (collect_all) {
+               git_all_attrs(full_path, check);
        } else {
-               if (git_all_attrs(full_path, &cnt, &check))
-                       die("git_all_attrs died");
-               output_attr(cnt, check, file);
-               free(check);
+               if (git_check_attr(full_path, check))
+                       die("git_check_attr died");
        }
+       output_attr(check, file);
+
        free(full_path);
 }
 
-static void check_attr_stdin_paths(const char *prefix, int cnt,
-       struct git_attr_check *check)
+static void check_attr_stdin_paths(const char *prefix,
+                                  struct attr_check *check,
+                                  int collect_all)
 {
        struct strbuf buf = STRBUF_INIT;
        struct strbuf unquoted = STRBUF_INIT;
@@ -84,7 +88,7 @@ static void check_attr_stdin_paths(const char *prefix, int cnt,
                                die("line is badly quoted");
                        strbuf_swap(&buf, &unquoted);
                }
-               check_attr(prefix, cnt, check, buf.buf);
+               check_attr(prefix, check, collect_all, buf.buf);
                maybe_flush_or_die(stdout, "attribute to stdout");
        }
        strbuf_release(&buf);
@@ -99,7 +103,7 @@ static NORETURN void error_with_usage(const char *msg)
 
 int cmd_check_attr(int argc, const char **argv, const char *prefix)
 {
-       struct git_attr_check *check;
+       struct attr_check *check;
        int cnt, i, doubledash, filei;
 
        if (!is_bare_repository())
@@ -159,28 +163,26 @@ int cmd_check_attr(int argc, const char **argv, const char *prefix)
                        error_with_usage("No file specified");
        }
 
-       if (all_attrs) {
-               check = NULL;
-       } else {
-               check = xcalloc(cnt, sizeof(*check));
+       check = attr_check_alloc();
+       if (!all_attrs) {
                for (i = 0; i < cnt; i++) {
-                       const char *name;
-                       struct git_attr *a;
-                       name = argv[i];
-                       a = git_attr(name);
+                       const struct git_attr *a = git_attr(argv[i]);
+
                        if (!a)
                                return error("%s: not a valid attribute name",
-                                       name);
-                       check[i].attr = a;
+                                            argv[i]);
+                       attr_check_append(check, a);
                }
        }
 
        if (stdin_paths)
-               check_attr_stdin_paths(prefix, cnt, check);
+               check_attr_stdin_paths(prefix, check, all_attrs);
        else {
                for (i = filei; i < argc; i++)
-                       check_attr(prefix, cnt, check, argv[i]);
+                       check_attr(prefix, check, all_attrs, argv[i]);
                maybe_flush_or_die(stdout, "attribute to stdout");
        }
+
+       attr_check_free(check);
        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 d6bc3aaaea0dfdbac2386d54f4ff2d25dbccf7e6..d861f836a29bf98d248f27632a8c47711a5da3a5 100644 (file)
@@ -174,8 +174,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                /* an empty dir could be removed even if it is unreadble */
                res = dry_run ? 0 : rmdir(path->buf);
                if (res) {
+                       int saved_errno = errno;
                        quote_path_relative(path->buf, prefix, &quoted);
-                       warning(_(msg_warn_remove_failed), quoted.buf);
+                       errno = saved_errno;
+                       warning_errno(_(msg_warn_remove_failed), quoted.buf);
                        *dir_gone = 0;
                }
                return res;
@@ -208,8 +210,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                                quote_path_relative(path->buf, prefix, &quoted);
                                string_list_append(&dels, quoted.buf);
                        } else {
+                               int saved_errno = errno;
                                quote_path_relative(path->buf, prefix, &quoted);
-                               warning(_(msg_warn_remove_failed), quoted.buf);
+                               errno = saved_errno;
+                               warning_errno(_(msg_warn_remove_failed), quoted.buf);
                                *dir_gone = 0;
                                ret = 1;
                        }
@@ -230,8 +234,10 @@ static int remove_dirs(struct strbuf *path, const char *prefix, int force_flag,
                if (!res)
                        *dir_gone = 1;
                else {
+                       int saved_errno = errno;
                        quote_path_relative(path->buf, prefix, &quoted);
-                       warning(_(msg_warn_remove_failed), quoted.buf);
+                       errno = saved_errno;
+                       warning_errno(_(msg_warn_remove_failed), quoted.buf);
                        *dir_gone = 0;
                        ret = 1;
                }
@@ -981,8 +987,10 @@ int cmd_clean(int argc, const char **argv, const char *prefix)
                } else {
                        res = dry_run ? 0 : unlink(abs_path.buf);
                        if (res) {
+                               int saved_errno = errno;
                                qname = quote_path_relative(item->string, NULL, &buf);
-                               warning(_(msg_warn_remove_failed), qname);
+                               errno = saved_errno;
+                               warning_errno(_(msg_warn_remove_failed), qname);
                                errors++;
                        } else if (!quiet) {
                                qname = quote_path_relative(item->string, NULL, &buf);
index 3f63edbbf94fdf83307621ed822722e5c9f89321..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"),
@@ -681,7 +699,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
 
 static int checkout(int submodule_progress)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        char *head;
        struct lock_file *lock_file;
        struct unpack_trees_options opts;
@@ -692,7 +710,7 @@ static int checkout(int submodule_progress)
        if (option_no_checkout)
                return 0;
 
-       head = resolve_refdup("HEAD", RESOLVE_REF_READING, sha1, NULL);
+       head = resolve_refdup("HEAD", RESOLVE_REF_READING, oid.hash, NULL);
        if (!head) {
                warning(_("remote HEAD refers to nonexistent ref, "
                          "unable to checkout.\n"));
@@ -700,7 +718,7 @@ static int checkout(int submodule_progress)
        }
        if (!strcmp(head, "HEAD")) {
                if (advice_detached_head)
-                       detach_advice(sha1_to_hex(sha1));
+                       detach_advice(oid_to_hex(&oid));
        } else {
                if (!starts_with(head, "refs/heads/"))
                        die(_("HEAD not found below refs/heads!"));
@@ -721,7 +739,7 @@ static int checkout(int submodule_progress)
        opts.src_index = &the_index;
        opts.dst_index = &the_index;
 
-       tree = parse_tree_indirect(sha1);
+       tree = parse_tree_indirect(oid.hash);
        parse_tree(tree);
        init_tree_desc(&t, tree->buffer, tree->size);
        if (unpack_trees(1, &t, &opts) < 0)
@@ -731,9 +749,9 @@ static int checkout(int submodule_progress)
                die(_("unable to write new index file"));
 
        err |= run_hook_le(NULL, "post-checkout", sha1_to_hex(null_sha1),
-                          sha1_to_hex(sha1), "1", NULL);
+                          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 2de5f6cc6401b48852d8174edcbf4e149cbe51cf..ad188fea9ec552c65c4b82c3558977dd6648fe77 100644 (file)
@@ -496,7 +496,7 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
 static int run_status(FILE *fp, const char *index_file, const char *prefix, int nowarn,
                      struct wt_status *s)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (s->relative_paths)
                s->prefix = prefix;
@@ -509,9 +509,9 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
        s->index_file = index_file;
        s->fp = fp;
        s->nowarn = nowarn;
-       s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
+       s->is_initial = get_sha1(s->reference, oid.hash) ? 1 : 0;
        if (!s->is_initial)
-               hashcpy(s->sha1_commit, sha1);
+               hashcpy(s->sha1_commit, oid.hash);
        s->status_format = status_format;
        s->ignore_submodule_arg = ignore_submodule_arg;
 
@@ -885,7 +885,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                commitable = run_status(s->fp, index_file, prefix, 1, s);
                s->use_color = saved_color_setting;
        } else {
-               unsigned char sha1[20];
+               struct object_id oid;
                const char *parent = "HEAD";
 
                if (!active_nr && read_cache() < 0)
@@ -894,7 +894,7 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                if (amend)
                        parent = "HEAD^1";
 
-               if (get_sha1(parent, sha1)) {
+               if (get_sha1(parent, oid.hash)) {
                        int i, ita_nr = 0;
 
                        for (i = 0; i < active_nr; i++)
@@ -1332,7 +1332,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 {
        static struct wt_status s;
        int fd;
-       unsigned char sha1[20];
+       struct object_id oid;
        static struct option builtin_status_options[] = {
                OPT__VERBOSE(&verbose, N_("be verbose")),
                OPT_SET_INT('s', "short", &status_format,
@@ -1382,9 +1382,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
 
        fd = hold_locked_index(&index_lock, 0);
 
-       s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
+       s.is_initial = get_sha1(s.reference, oid.hash) ? 1 : 0;
        if (!s.is_initial)
-               hashcpy(s.sha1_commit, sha1);
+               hashcpy(s.sha1_commit, oid.hash);
 
        s.ignore_submodule_arg = ignore_submodule_arg;
        s.status_format = status_format;
@@ -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);
 
@@ -1418,19 +1418,19 @@ static const char *implicit_ident_advice(void)
 
 }
 
-static void print_summary(const char *prefix, const unsigned char *sha1,
+static void print_summary(const char *prefix, const struct object_id *oid,
                          int initial_commit)
 {
        struct rev_info rev;
        struct commit *commit;
        struct strbuf format = STRBUF_INIT;
-       unsigned char junk_sha1[20];
+       struct object_id junk_oid;
        const char *head;
        struct pretty_print_context pctx = {0};
        struct strbuf author_ident = STRBUF_INIT;
        struct strbuf committer_ident = STRBUF_INIT;
 
-       commit = lookup_commit(sha1);
+       commit = lookup_commit(oid->hash);
        if (!commit)
                die(_("couldn't look up newly created commit"));
        if (parse_commit(commit))
@@ -1477,7 +1477,7 @@ static void print_summary(const char *prefix, const unsigned char *sha1,
        rev.diffopt.break_opt = 0;
        diff_setup_done(&rev.diffopt);
 
-       head = resolve_ref_unsafe("HEAD", 0, junk_sha1, NULL);
+       head = resolve_ref_unsafe("HEAD", 0, junk_oid.hash, NULL);
        if (!strcmp(head, "HEAD"))
                head = _("detached HEAD");
        else
@@ -1522,8 +1522,8 @@ static int git_commit_config(const char *k, const char *v, void *cb)
        return git_status_config(k, v, s);
 }
 
-static int run_rewrite_hook(const unsigned char *oldsha1,
-                           const unsigned char *newsha1)
+static int run_rewrite_hook(const struct object_id *oldoid,
+                           const struct object_id *newoid)
 {
        struct child_process proc = CHILD_PROCESS_INIT;
        const char *argv[3];
@@ -1544,7 +1544,7 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
        code = start_command(&proc);
        if (code)
                return code;
-       strbuf_addf(&sb, "%s %s\n", sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+       strbuf_addf(&sb, "%s %s\n", oid_to_hex(oldoid), oid_to_hex(newoid));
        sigchain_push(SIGPIPE, SIG_IGN);
        write_in_full(proc.in, sb.buf, sb.len);
        close(proc.in);
@@ -1636,7 +1636,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        struct strbuf author_ident = STRBUF_INIT;
        const char *index_file, *reflog_msg;
        char *nl;
-       unsigned char sha1[20];
+       struct object_id oid;
        struct commit_list *parents = NULL;
        struct stat statbuf;
        struct commit *current_head = NULL;
@@ -1651,10 +1651,10 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        status_format = STATUS_FORMAT_NONE; /* Ignore status.short */
        s.colopts = 0;
 
-       if (get_sha1("HEAD", sha1))
+       if (get_sha1("HEAD", oid.hash))
                current_head = NULL;
        else {
-               current_head = lookup_commit_or_die(sha1, "HEAD");
+               current_head = lookup_commit_or_die(oid.hash, "HEAD");
                if (parse_commit(current_head))
                        die(_("could not parse HEAD commit"));
        }
@@ -1759,7 +1759,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        }
 
        if (commit_tree_extended(sb.buf, sb.len, active_cache_tree->sha1,
-                        parents, sha1, author_ident.buf, sign_commit, extra)) {
+                        parents, oid.hash, author_ident.buf, sign_commit, extra)) {
                rollback_index_files();
                die(_("failed to write commit object"));
        }
@@ -1776,7 +1776,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
 
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
-           ref_transaction_update(transaction, "HEAD", sha1,
+           ref_transaction_update(transaction, "HEAD", oid.hash,
                                   current_head
                                   ? current_head->object.oid.hash : null_sha1,
                                   0, sb.buf, &err) ||
@@ -1805,13 +1805,13 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
                cfg = init_copy_notes_for_rewrite("amend");
                if (cfg) {
                        /* we are amending, so current_head is not NULL */
-                       copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1);
+                       copy_note_for_rewrite(cfg, current_head->object.oid.hash, oid.hash);
                        finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
                }
-               run_rewrite_hook(current_head->object.oid.hash, sha1);
+               run_rewrite_hook(&current_head->object.oid, &oid);
        }
        if (!quiet)
-               print_summary(prefix, sha1, !current_head);
+               print_summary(prefix, &oid, !current_head);
 
        strbuf_release(&err);
        return 0;
index 05843a0f96e4dc0dbf9fbc7310039794b57947e7..1b7ed5af0839d25e063e19c9a83b4233a7c2a3ca 100644 (file)
@@ -26,7 +26,8 @@ static int use_global_config, use_system_config, use_local_config;
 static struct git_config_source given_config_source;
 static int actions, types;
 static int end_null;
-static int respect_includes = -1;
+static int respect_includes_opt = -1;
+static struct config_options config_options;
 static int show_origin;
 
 #define ACTION_GET (1<<0)
@@ -81,7 +82,7 @@ static struct option builtin_config_options[] = {
        OPT_GROUP(N_("Other")),
        OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
-       OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
+       OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
        OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
        OPT_END(),
 };
@@ -242,7 +243,7 @@ static int get_value(const char *key_, const char *regex_)
        }
 
        git_config_with_options(collect_config, &values,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        ret = !values.nr;
 
@@ -320,7 +321,7 @@ static void get_color(const char *var, const char *def_color)
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        if (!get_color_found && def_color) {
                if (color_parse(def_color, parsed_color) < 0)
@@ -352,7 +353,7 @@ static int get_colorbool(const char *var, int print)
        get_diff_color_found = -1;
        get_color_ui_found = -1;
        git_config_with_options(git_get_colorbool_config, NULL,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        if (get_colorbool_found < 0) {
                if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -441,7 +442,7 @@ static int get_urlmatch(const char *var, const char *url)
        }
 
        git_config_with_options(urlmatch_config_entry, &config,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        ret = !values.nr;
 
@@ -502,7 +503,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,13 +528,13 @@ 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)
-               respect_includes = !given_config_source.file;
+       if (respect_includes_opt == -1)
+               config_options.respect_includes = !given_config_source.file;
+       else
+               config_options.respect_includes = respect_includes_opt;
 
        if (end_null) {
                term = '\0';
@@ -580,7 +581,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
                                            &given_config_source,
-                                           respect_includes) < 0) {
+                                           &config_options) < 0) {
                        if (given_config_source.file)
                                die_errno("unable to read config file '%s'",
                                          given_config_source.file);
index a04b4f2ef337d4d9d8cb24de7288630ee8ab823c..acb05940fc3cd902d4c9bfa480f2c473dcc4d9d5 100644 (file)
@@ -53,7 +53,7 @@ static void loose_garbage(const char *path)
                report_garbage(PACKDIR_FILE_GARBAGE, path);
 }
 
-static int count_loose(const unsigned char *sha1, const char *path, void *data)
+static int count_loose(const struct object_id *oid, const char *path, void *data)
 {
        struct stat st;
 
@@ -62,7 +62,7 @@ static int count_loose(const unsigned char *sha1, const char *path, void *data)
        else {
                loose_size += on_disk_bytes(st);
                loose++;
-               if (verbose && has_sha1_pack(sha1))
+               if (verbose && has_sha1_pack(oid->hash))
                        packed_loose++;
        }
        return 0;
index 01490a157efc5d85626833c0b1c25b75fb7ee469..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)
@@ -28,9 +29,10 @@ static int abbrev = -1; /* unspecified */
 static int max_candidates = 10;
 static struct hashmap names;
 static int have_util;
-static const char *pattern;
+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[] = {
@@ -39,32 +41,32 @@ static const char *diff_index_args[] = {
 
 struct commit_name {
        struct hashmap_entry entry;
-       unsigned char peeled[20];
+       struct object_id peeled;
        struct tag *tag;
        unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
        unsigned name_checked:1;
-       unsigned char sha1[20];
+       struct object_id oid;
        char *path;
 };
 
 static const char *prio_names[] = {
-       "head", "lightweight", "annotated",
+       N_("head"), N_("lightweight"), N_("annotated"),
 };
 
 static int commit_name_cmp(const struct commit_name *cn1,
                const struct commit_name *cn2, const void *peeled)
 {
-       return hashcmp(cn1->peeled, peeled ? peeled : cn2->peeled);
+       return oidcmp(&cn1->peeled, peeled ? peeled : &cn2->peeled);
 }
 
-static inline struct commit_name *find_commit_name(const unsigned char *peeled)
+static inline struct commit_name *find_commit_name(const struct object_id *peeled)
 {
-       return hashmap_get_from_hash(&names, sha1hash(peeled), peeled);
+       return hashmap_get_from_hash(&names, sha1hash(peeled->hash), peeled->hash);
 }
 
 static int replace_name(struct commit_name *e,
                               int prio,
-                              const unsigned char *sha1,
+                              const struct object_id *oid,
                               struct tag **tag)
 {
        if (!e || e->prio < prio)
@@ -77,13 +79,13 @@ static int replace_name(struct commit_name *e,
                struct tag *t;
 
                if (!e->tag) {
-                       t = lookup_tag(e->sha1);
+                       t = lookup_tag(e->oid.hash);
                        if (!t || parse_tag(t))
                                return 1;
                        e->tag = t;
                }
 
-               t = lookup_tag(sha1);
+               t = lookup_tag(oid->hash);
                if (!t || parse_tag(t))
                        return 0;
                *tag = t;
@@ -96,24 +98,24 @@ static int replace_name(struct commit_name *e,
 }
 
 static void add_to_known_names(const char *path,
-                              const unsigned char *peeled,
+                              const struct object_id *peeled,
                               int prio,
-                              const unsigned char *sha1)
+                              const struct object_id *oid)
 {
        struct commit_name *e = find_commit_name(peeled);
        struct tag *tag = NULL;
-       if (replace_name(e, prio, sha1, &tag)) {
+       if (replace_name(e, prio, oid, &tag)) {
                if (!e) {
                        e = xmalloc(sizeof(struct commit_name));
-                       hashcpy(e->peeled, peeled);
-                       hashmap_entry_init(e, sha1hash(peeled));
+                       oidcpy(&e->peeled, peeled);
+                       hashmap_entry_init(e, sha1hash(peeled->hash));
                        hashmap_add(&names, e);
                        e->path = NULL;
                }
                e->tag = tag;
                e->prio = prio;
                e->name_checked = 0;
-               hashcpy(e->sha1, sha1);
+               oidcpy(&e->oid, oid);
                free(e->path);
                e->path = xstrdup(path);
        }
@@ -129,9 +131,40 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
        if (!all && !is_tag)
                return 0;
 
-       /* Accept only tags that match the pattern, if given */
-       if (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL)))
-               return 0;
+       /*
+        * If we're given exclude patterns, first exclude any tag which match
+        * any of the exclude pattern.
+        */
+       if (exclude_patterns.nr) {
+               struct string_list_item *item;
+
+               if (!is_tag)
+                       return 0;
+
+               for_each_string_list_item(item, &exclude_patterns) {
+                       if (!wildmatch(item->string, path + 10, 0, NULL))
+                               return 0;
+               }
+       }
+
+       /*
+        * If we're given patterns, accept only tags which match at least one
+        * pattern.
+        */
+       if (patterns.nr) {
+               struct string_list_item *item;
+
+               if (!is_tag)
+                       return 0;
+
+               for_each_string_list_item(item, &patterns) {
+                       if (!wildmatch(item->string, path + 10, 0, NULL))
+                               break;
+
+                       /* If we get here, no pattern matched. */
+                       return 0;
+               }
+       }
 
        /* Is it annotated? */
        if (!peel_ref(path, peeled.hash)) {
@@ -154,7 +187,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
        else
                prio = 0;
 
-       add_to_known_names(all ? path + 5 : path + 10, peeled.hash, prio, oid->hash);
+       add_to_known_names(all ? path + 5 : path + 10, &peeled, prio, oid);
        return 0;
 }
 
@@ -212,7 +245,7 @@ static unsigned long finish_depth_computation(
 static void display_name(struct commit_name *n)
 {
        if (n->prio == 2 && !n->tag) {
-               n->tag = lookup_tag(n->sha1);
+               n->tag = lookup_tag(n->oid.hash);
                if (!n->tag || parse_tag(n->tag))
                        die(_("annotated tag %s not available"), n->path);
        }
@@ -230,14 +263,14 @@ static void display_name(struct commit_name *n)
                printf("%s", n->path);
 }
 
-static void show_suffix(int depth, const unsigned char *sha1)
+static void show_suffix(int depth, const struct object_id *oid)
 {
-       printf("-%d-g%s", depth, find_unique_abbrev(sha1, abbrev));
+       printf("-%d-g%s", depth, find_unique_abbrev(oid->hash, abbrev));
 }
 
 static void describe(const char *arg, int last_one)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        struct commit *cmit, *gave_up_on = NULL;
        struct commit_list *list;
        struct commit_name *n;
@@ -246,22 +279,22 @@ static void describe(const char *arg, int last_one)
        unsigned long seen_commits = 0;
        unsigned int unannotated_cnt = 0;
 
-       if (get_sha1(arg, sha1))
+       if (get_oid(arg, &oid))
                die(_("Not a valid object name %s"), arg);
-       cmit = lookup_commit_reference(sha1);
+       cmit = lookup_commit_reference(oid.hash);
        if (!cmit)
                die(_("%s is not a valid '%s' object"), arg, commit_type);
 
-       n = find_commit_name(cmit->object.oid.hash);
+       n = find_commit_name(&cmit->object.oid);
        if (n && (tags || all || n->prio == 2)) {
                /*
                 * Exact match to an existing ref.
                 */
                display_name(n);
                if (longformat)
-                       show_suffix(0, n->tag ? n->tag->tagged->oid.hash : sha1);
-               if (dirty)
-                       printf("%s", dirty);
+                       show_suffix(0, n->tag ? &n->tag->tagged->oid : &oid);
+               if (suffix)
+                       printf("%s", suffix);
                printf("\n");
                return;
        }
@@ -276,7 +309,7 @@ static void describe(const char *arg, int last_one)
                struct commit *c;
                struct commit_name *n = hashmap_iter_first(&names, &iter);
                for (; n; n = hashmap_iter_next(&iter)) {
-                       c = lookup_commit_reference_gently(n->peeled, 1);
+                       c = lookup_commit_reference_gently(n->peeled.hash, 1);
                        if (c)
                                c->util = n;
                }
@@ -337,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;
                }
@@ -362,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);
@@ -380,9 +422,9 @@ 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.hash);
-       if (dirty)
-               printf("%s", dirty);
+               show_suffix(all_matches[0].depth, &cmit->object.oid);
+       if (suffix)
+               printf("%s", suffix);
        printf("\n");
 
        if (!last_one)
@@ -404,13 +446,18 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                            N_("only output exact matches"), 0),
                OPT_INTEGER(0, "candidates", &max_candidates,
                            N_("consider <n> most recent tags (default: 10)")),
-               OPT_STRING(0, "match",       &pattern, N_("pattern"),
+               OPT_STRING_LIST(0, "match", &patterns, N_("pattern"),
                           N_("only consider tags matching <pattern>")),
+               OPT_STRING_LIST(0, "exclude", &exclude_patterns, N_("pattern"),
+                          N_("do not consider tags matching <pattern>")),
                OPT_BOOL(0, "always",        &always,
                        N_("show abbreviated commit object as fallback")),
                {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(),
        };
 
@@ -430,6 +477,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                die(_("--long is incompatible with --abbrev=0"));
 
        if (contains) {
+               struct string_list_item *item;
                struct argv_array args;
 
                argv_array_init(&args);
@@ -440,8 +488,10 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
                        argv_array_push(&args, "--always");
                if (!all) {
                        argv_array_push(&args, "--tags");
-                       if (pattern)
-                               argv_array_pushf(&args, "--refs=refs/tags/%s", pattern);
+                       for_each_string_list_item(item, &patterns)
+                               argv_array_pushf(&args, "--refs=refs/tags/%s", item->string);
+                       for_each_string_list_item(item, &exclude_patterns)
+                               argv_array_pushf(&args, "--exclude=refs/tags/%s", item->string);
                }
                if (argc)
                        argv_array_pushv(&args, argv);
@@ -456,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;
 
@@ -469,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 8ce00480cd484e10b0b46d931c934fcd3ba7b71e..326f88b6576d6c02e9a0bcecb5a0b402bb257372 100644 (file)
@@ -7,46 +7,44 @@
 
 static struct rev_info log_tree_opt;
 
-static int diff_tree_commit_sha1(const unsigned char *sha1)
+static int diff_tree_commit_sha1(const struct object_id *oid)
 {
-       struct commit *commit = lookup_commit_reference(sha1);
+       struct commit *commit = lookup_commit_reference(oid->hash);
        if (!commit)
                return -1;
        return log_tree_commit(&log_tree_opt, commit);
 }
 
 /* Diff one or more commits. */
-static int stdin_diff_commit(struct commit *commit, char *line, int len)
+static int stdin_diff_commit(struct commit *commit, const char *p)
 {
-       unsigned char sha1[20];
-       if (isspace(line[40]) && !get_sha1_hex(line+41, sha1)) {
-               /* Graft the fake parents locally to the commit */
-               int pos = 41;
-               struct commit_list **pptr;
-
-               /* Free the real parent list */
-               free_commit_list(commit->parents);
-               commit->parents = NULL;
-               pptr = &(commit->parents);
-               while (line[pos] && !get_sha1_hex(line + pos, sha1)) {
-                       struct commit *parent = lookup_commit(sha1);
-                       if (parent) {
-                               pptr = &commit_list_insert(parent, pptr)->next;
-                       }
-                       pos += 41;
+       struct object_id oid;
+       struct commit_list **pptr = NULL;
+
+       /* Graft the fake parents locally to the commit */
+       while (isspace(*p++) && !parse_oid_hex(p, &oid, &p)) {
+               struct commit *parent = lookup_commit(oid.hash);
+               if (!pptr) {
+                       /* Free the real parent list */
+                       free_commit_list(commit->parents);
+                       commit->parents = NULL;
+                       pptr = &(commit->parents);
+               }
+               if (parent) {
+                       pptr = &commit_list_insert(parent, pptr)->next;
                }
        }
        return log_tree_commit(&log_tree_opt, commit);
 }
 
 /* Diff two trees. */
-static int stdin_diff_trees(struct tree *tree1, char *line, int len)
+static int stdin_diff_trees(struct tree *tree1, const char *p)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        struct tree *tree2;
-       if (len != 82 || !isspace(line[40]) || get_sha1_hex(line + 41, sha1))
+       if (!isspace(*p++) || parse_oid_hex(p, &oid, &p) || *p)
                return error("Need exactly two trees, separated by a space");
-       tree2 = lookup_tree(sha1);
+       tree2 = lookup_tree(oid.hash);
        if (!tree2 || parse_tree(tree2))
                return -1;
        printf("%s %s\n", oid_to_hex(&tree1->object.oid),
@@ -60,23 +58,24 @@ static int stdin_diff_trees(struct tree *tree1, char *line, int len)
 static int diff_tree_stdin(char *line)
 {
        int len = strlen(line);
-       unsigned char sha1[20];
+       struct object_id oid;
        struct object *obj;
+       const char *p;
 
        if (!len || line[len-1] != '\n')
                return -1;
        line[len-1] = 0;
-       if (get_sha1_hex(line, sha1))
+       if (parse_oid_hex(line, &oid, &p))
                return -1;
-       obj = parse_object(sha1);
+       obj = parse_object(oid.hash);
        if (!obj)
                return -1;
        if (obj->type == OBJ_COMMIT)
-               return stdin_diff_commit((struct commit *)obj, line, len);
+               return stdin_diff_commit((struct commit *)obj, p);
        if (obj->type == OBJ_TREE)
-               return stdin_diff_trees((struct tree *)obj, line, len);
+               return stdin_diff_trees((struct tree *)obj, p);
        error("Object %s is a %s, not a commit or tree",
-             sha1_to_hex(sha1), typename(obj->type));
+             oid_to_hex(&oid), typename(obj->type));
        return -1;
 }
 
@@ -141,7 +140,7 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                break;
        case 1:
                tree1 = opt->pending.objects[0].item;
-               diff_tree_commit_sha1(tree1->oid.hash);
+               diff_tree_commit_sha1(&tree1->oid);
                break;
        case 2:
                tree1 = opt->pending.objects[0].item;
@@ -164,9 +163,9 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                        opt->diffopt.setup |= (DIFF_SETUP_USE_SIZE_CACHE |
                                               DIFF_SETUP_USE_CACHE);
                while (fgets(line, sizeof(line), stdin)) {
-                       unsigned char sha1[20];
+                       struct object_id oid;
 
-                       if (get_sha1_hex(line, sha1)) {
+                       if (get_oid_hex(line, &oid)) {
                                fputs(line, stdout);
                                fflush(stdout);
                        }
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 1e815b5577cc05a6133ded56dda4b84596f80514..e0220630d00cf2c069ce1a43e9511df57685acd2 100644 (file)
@@ -212,7 +212,7 @@ static char *anonymize_blob(unsigned long *size)
        return strbuf_detach(&out, NULL);
 }
 
-static void export_blob(const unsigned char *sha1)
+static void export_blob(const struct object_id *oid)
 {
        unsigned long size;
        enum object_type type;
@@ -223,34 +223,34 @@ static void export_blob(const unsigned char *sha1)
        if (no_data)
                return;
 
-       if (is_null_sha1(sha1))
+       if (is_null_oid(oid))
                return;
 
-       object = lookup_object(sha1);
+       object = lookup_object(oid->hash);
        if (object && object->flags & SHOWN)
                return;
 
        if (anonymize) {
                buf = anonymize_blob(&size);
-               object = (struct object *)lookup_blob(sha1);
+               object = (struct object *)lookup_blob(oid->hash);
                eaten = 0;
        } else {
-               buf = read_sha1_file(sha1, &type, &size);
+               buf = read_sha1_file(oid->hash, &type, &size);
                if (!buf)
-                       die ("Could not read blob %s", sha1_to_hex(sha1));
-               if (check_sha1_signature(sha1, buf, size, typename(type)) < 0)
-                       die("sha1 mismatch in blob %s", sha1_to_hex(sha1));
-               object = parse_object_buffer(sha1, type, size, buf, &eaten);
+                       die ("Could not read blob %s", oid_to_hex(oid));
+               if (check_sha1_signature(oid->hash, buf, size, typename(type)) < 0)
+                       die("sha1 mismatch in blob %s", oid_to_hex(oid));
+               object = parse_object_buffer(oid->hash, type, size, buf, &eaten);
        }
 
        if (!object)
-               die("Could not read blob %s", sha1_to_hex(sha1));
+               die("Could not read blob %s", oid_to_hex(oid));
 
        mark_next_object(object);
 
        printf("blob\nmark :%"PRIu32"\ndata %lu\n", last_idnum, size);
        if (size && fwrite(buf, size, 1, stdout) != 1)
-               die_errno ("Could not write blob '%s'", sha1_to_hex(sha1));
+               die_errno ("Could not write blob '%s'", oid_to_hex(oid));
        printf("\n");
 
        show_progress();
@@ -323,19 +323,19 @@ static void print_path(const char *path)
        }
 }
 
-static void *generate_fake_sha1(const void *old, size_t *len)
+static void *generate_fake_oid(const void *old, size_t *len)
 {
        static uint32_t counter = 1; /* avoid null sha1 */
-       unsigned char *out = xcalloc(20, 1);
-       put_be32(out + 16, counter++);
+       unsigned char *out = xcalloc(GIT_SHA1_RAWSZ, 1);
+       put_be32(out + GIT_SHA1_RAWSZ - 4, counter++);
        return out;
 }
 
-static const unsigned char *anonymize_sha1(const unsigned char *sha1)
+static const unsigned char *anonymize_sha1(const struct object_id *oid)
 {
        static struct hashmap sha1s;
-       size_t len = 20;
-       return anonymize_mem(&sha1s, generate_fake_sha1, sha1, &len);
+       size_t len = GIT_SHA1_RAWSZ;
+       return anonymize_mem(&sha1s, generate_fake_oid, oid, &len);
 }
 
 static void show_filemodify(struct diff_queue_struct *q,
@@ -383,7 +383,7 @@ static void show_filemodify(struct diff_queue_struct *q,
                        if (no_data || S_ISGITLINK(spec->mode))
                                printf("M %06o %s ", spec->mode,
                                       sha1_to_hex(anonymize ?
-                                                  anonymize_sha1(spec->oid.hash) :
+                                                  anonymize_sha1(&spec->oid) :
                                                   spec->oid.hash));
                        else {
                                struct object *object = lookup_object(spec->oid.hash);
@@ -572,7 +572,7 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        /* Export the referenced blobs, and remember the marks. */
        for (i = 0; i < diff_queued_diff.nr; i++)
                if (!S_ISGITLINK(diff_queued_diff.queue[i]->two->mode))
-                       export_blob(diff_queued_diff.queue[i]->two->oid.hash);
+                       export_blob(&diff_queued_diff.queue[i]->two->oid);
 
        refname = commit->util;
        if (anonymize) {
@@ -797,14 +797,14 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
 
        for (i = 0; i < info->nr; i++) {
                struct rev_cmdline_entry *e = info->rev + i;
-               unsigned char sha1[20];
+               struct object_id oid;
                struct commit *commit;
                char *full_name;
 
                if (e->flags & UNINTERESTING)
                        continue;
 
-               if (dwim_ref(e->name, strlen(e->name), sha1, &full_name) != 1)
+               if (dwim_ref(e->name, strlen(e->name), oid.hash, &full_name) != 1)
                        continue;
 
                if (refspecs) {
@@ -828,7 +828,7 @@ static void get_tags_and_duplicates(struct rev_cmdline_info *info)
                case OBJ_COMMIT:
                        break;
                case OBJ_BLOB:
-                       export_blob(commit->object.oid.hash);
+                       export_blob(&commit->object.oid);
                        continue;
                default: /* OBJ_TAG (nested tags) is already handled */
                        warning("Tag points to object of unexpected type %s, skipping.",
@@ -912,7 +912,7 @@ static void import_marks(char *input_file)
        while (fgets(line, sizeof(line), f)) {
                uint32_t mark;
                char *line_end, *mark_end;
-               unsigned char sha1[20];
+               struct object_id oid;
                struct object *object;
                struct commit *commit;
                enum object_type type;
@@ -924,28 +924,28 @@ static void import_marks(char *input_file)
 
                mark = strtoumax(line + 1, &mark_end, 10);
                if (!mark || mark_end == line + 1
-                       || *mark_end != ' ' || get_sha1_hex(mark_end + 1, sha1))
+                       || *mark_end != ' ' || get_oid_hex(mark_end + 1, &oid))
                        die("corrupt mark line: %s", line);
 
                if (last_idnum < mark)
                        last_idnum = mark;
 
-               type = sha1_object_info(sha1, NULL);
+               type = sha1_object_info(oid.hash, NULL);
                if (type < 0)
-                       die("object not found: %s", sha1_to_hex(sha1));
+                       die("object not found: %s", oid_to_hex(&oid));
 
                if (type != OBJ_COMMIT)
                        /* only commits */
                        continue;
 
-               commit = lookup_commit(sha1);
+               commit = lookup_commit(oid.hash);
                if (!commit)
-                       die("not a commit? can't happen: %s", sha1_to_hex(sha1));
+                       die("not a commit? can't happen: %s", oid_to_hex(&oid));
 
                object = &commit->object;
 
                if (object->flags & SHOWN)
-                       error("Object %s already has a mark", sha1_to_hex(sha1));
+                       error("Object %s already has a mark", oid_to_hex(&oid));
 
                mark_object(object, mark);
 
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 efab62fd85e314ccb6ec5becedead91823d03dde..6faa3c0d2424600a5e69dd20466aa763d95b586c 100644 (file)
@@ -41,7 +41,7 @@ struct src_data {
 };
 
 struct origin_data {
-       unsigned char sha1[20];
+       struct object_id oid;
        unsigned is_local_branch:1;
 };
 
@@ -59,8 +59,8 @@ static struct string_list origins = STRING_LIST_INIT_DUP;
 struct merge_parents {
        int alloc, nr;
        struct merge_parent {
-               unsigned char given[20];
-               unsigned char commit[20];
+               struct object_id given;
+               struct object_id commit;
                unsigned char used;
        } *item;
 };
@@ -70,14 +70,14 @@ struct merge_parents {
  * hundreds of heads at a time anyway.
  */
 static struct merge_parent *find_merge_parent(struct merge_parents *table,
-                                             unsigned char *given,
-                                             unsigned char *commit)
+                                             struct object_id *given,
+                                             struct object_id *commit)
 {
        int i;
        for (i = 0; i < table->nr; i++) {
-               if (given && hashcmp(table->item[i].given, given))
+               if (given && oidcmp(&table->item[i].given, given))
                        continue;
-               if (commit && hashcmp(table->item[i].commit, commit))
+               if (commit && oidcmp(&table->item[i].commit, commit))
                        continue;
                return &table->item[i];
        }
@@ -85,14 +85,14 @@ static struct merge_parent *find_merge_parent(struct merge_parents *table,
 }
 
 static void add_merge_parent(struct merge_parents *table,
-                            unsigned char *given,
-                            unsigned char *commit)
+                            struct object_id *given,
+                            struct object_id *commit)
 {
        if (table->nr && find_merge_parent(table, given, commit))
                return;
        ALLOC_GROW(table->item, table->nr + 1, table->alloc);
-       hashcpy(table->item[table->nr].given, given);
-       hashcpy(table->item[table->nr].commit, commit);
+       oidcpy(&table->item[table->nr].given, given);
+       oidcpy(&table->item[table->nr].commit, commit);
        table->item[table->nr].used = 0;
        table->nr++;
 }
@@ -106,30 +106,30 @@ static int handle_line(char *line, struct merge_parents *merge_parents)
        struct src_data *src_data;
        struct string_list_item *item;
        int pulling_head = 0;
-       unsigned char sha1[20];
+       struct object_id oid;
 
-       if (len < 43 || line[40] != '\t')
+       if (len < GIT_SHA1_HEXSZ + 3 || line[GIT_SHA1_HEXSZ] != '\t')
                return 1;
 
-       if (starts_with(line + 41, "not-for-merge"))
+       if (starts_with(line + GIT_SHA1_HEXSZ + 1, "not-for-merge"))
                return 0;
 
-       if (line[41] != '\t')
+       if (line[GIT_SHA1_HEXSZ + 1] != '\t')
                return 2;
 
-       i = get_sha1_hex(line, sha1);
+       i = get_oid_hex(line, &oid);
        if (i)
                return 3;
 
-       if (!find_merge_parent(merge_parents, sha1, NULL))
+       if (!find_merge_parent(merge_parents, &oid, NULL))
                return 0; /* subsumed by other parents */
 
        origin_data = xcalloc(1, sizeof(struct origin_data));
-       hashcpy(origin_data->sha1, sha1);
+       oidcpy(&origin_data->oid, &oid);
 
        if (line[len - 1] == '\n')
                line[len - 1] = 0;
-       line += 42;
+       line += GIT_SHA1_HEXSZ + 2;
 
        /*
         * At this point, line points at the beginning of comment e.g.
@@ -338,10 +338,10 @@ static void shortlog(const char *name,
        struct string_list committers = STRING_LIST_INIT_DUP;
        int flags = UNINTERESTING | TREESAME | SEEN | SHOWN | ADDED;
        struct strbuf sb = STRBUF_INIT;
-       const unsigned char *sha1 = origin_data->sha1;
+       const struct object_id *oid = &origin_data->oid;
        int limit = opts->shortlog_len;
 
-       branch = deref_tag(parse_object(sha1), sha1_to_hex(sha1), 40);
+       branch = deref_tag(parse_object(oid->hash), oid_to_hex(oid), GIT_SHA1_HEXSZ);
        if (!branch || branch->type != OBJ_COMMIT)
                return;
 
@@ -531,7 +531,7 @@ static void fmt_merge_msg_sigs(struct strbuf *out)
 }
 
 static void find_merge_parents(struct merge_parents *result,
-                              struct strbuf *in, unsigned char *head)
+                              struct strbuf *in, struct object_id *head)
 {
        struct commit_list *parents;
        struct commit *head_commit;
@@ -542,31 +542,31 @@ static void find_merge_parents(struct merge_parents *result,
                int len;
                char *p = in->buf + pos;
                char *newline = strchr(p, '\n');
-               unsigned char sha1[20];
+               struct object_id oid;
                struct commit *parent;
                struct object *obj;
 
                len = newline ? newline - p : strlen(p);
                pos += len + !!newline;
 
-               if (len < 43 ||
-                   get_sha1_hex(p, sha1) ||
-                   p[40] != '\t' ||
-                   p[41] != '\t')
+               if (len < GIT_SHA1_HEXSZ + 3 ||
+                   get_oid_hex(p, &oid) ||
+                   p[GIT_SHA1_HEXSZ] != '\t' ||
+                   p[GIT_SHA1_HEXSZ + 1] != '\t')
                        continue; /* skip not-for-merge */
                /*
                 * Do not use get_merge_parent() here; we do not have
                 * "name" here and we do not want to contaminate its
                 * util field yet.
                 */
-               obj = parse_object(sha1);
+               obj = parse_object(oid.hash);
                parent = (struct commit *)peel_to_type(NULL, 0, obj, OBJ_COMMIT);
                if (!parent)
                        continue;
                commit_list_insert(parent, &parents);
-               add_merge_parent(result, obj->oid.hash, parent->object.oid.hash);
+               add_merge_parent(result, &obj->oid, &parent->object.oid);
        }
-       head_commit = lookup_commit(head);
+       head_commit = lookup_commit(head->hash);
        if (head_commit)
                commit_list_insert(head_commit, &parents);
        parents = reduce_heads(parents);
@@ -574,7 +574,7 @@ static void find_merge_parents(struct merge_parents *result,
        while (parents) {
                struct commit *cmit = pop_commit(&parents);
                for (i = 0; i < result->nr; i++)
-                       if (!hashcmp(result->item[i].commit, cmit->object.oid.hash))
+                       if (!oidcmp(&result->item[i].commit, &cmit->object.oid))
                                result->item[i].used = 1;
        }
 
@@ -592,7 +592,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                  struct fmt_merge_msg_opts *opts)
 {
        int i = 0, pos = 0;
-       unsigned char head_sha1[20];
+       struct object_id head_oid;
        const char *current_branch;
        void *current_branch_to_free;
        struct merge_parents merge_parents;
@@ -601,13 +601,13 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 
        /* get current branch */
        current_branch = current_branch_to_free =
-               resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
+               resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL);
        if (!current_branch)
                die("No current branch");
        if (starts_with(current_branch, "refs/heads/"))
                current_branch += 11;
 
-       find_merge_parents(&merge_parents, in, head_sha1);
+       find_merge_parents(&merge_parents, in, &head_oid);
 
        /* get a line */
        while (pos < in->len) {
@@ -633,7 +633,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
                struct commit *head;
                struct rev_info rev;
 
-               head = lookup_commit_or_die(head_sha1, "HEAD");
+               head = lookup_commit_or_die(head_oid.hash, "HEAD");
                init_revisions(&rev, NULL);
                rev.commit_format = CMIT_FMT_ONELINE;
                rev.ignore_merges = 1;
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 1a5caccd0f5ee3fa56af06e8f536271f58312665..b5e13a45560f9338a65191c22d213f33052bf9b9 100644 (file)
@@ -396,13 +396,13 @@ static int fsck_obj_buffer(const unsigned char *sha1, enum object_type type,
 
 static int default_refs;
 
-static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
+static void fsck_handle_reflog_oid(const char *refname, struct object_id *oid,
        unsigned long timestamp)
 {
        struct object *obj;
 
-       if (!is_null_sha1(sha1)) {
-               obj = lookup_object(sha1);
+       if (!is_null_oid(oid)) {
+               obj = lookup_object(oid->hash);
                if (obj && (obj->flags & HAS_OBJ)) {
                        if (timestamp && name_objects)
                                add_decoration(fsck_walk_options.object_names,
@@ -411,13 +411,13 @@ static void fsck_handle_reflog_sha1(const char *refname, unsigned char *sha1,
                        obj->used = 1;
                        mark_object_reachable(obj);
                } else {
-                       error("%s: invalid reflog entry %s", refname, sha1_to_hex(sha1));
+                       error("%s: invalid reflog entry %s", refname, oid_to_hex(oid));
                        errors_found |= ERROR_REACHABLE;
                }
        }
 }
 
-static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int fsck_handle_reflog_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
@@ -425,10 +425,10 @@ static int fsck_handle_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 
        if (verbose)
                fprintf(stderr, "Checking reflog %s->%s\n",
-                       sha1_to_hex(osha1), sha1_to_hex(nsha1));
+                       oid_to_hex(ooid), oid_to_hex(noid));
 
-       fsck_handle_reflog_sha1(refname, osha1, 0);
-       fsck_handle_reflog_sha1(refname, nsha1, timestamp);
+       fsck_handle_reflog_oid(refname, ooid, 0);
+       fsck_handle_reflog_oid(refname, noid, timestamp);
        return 0;
 }
 
@@ -491,7 +491,7 @@ static void get_default_heads(void)
        }
 }
 
-static struct object *parse_loose_object(const unsigned char *sha1,
+static struct object *parse_loose_object(const struct object_id *oid,
                                         const char *path)
 {
        struct object *obj;
@@ -500,27 +500,27 @@ static struct object *parse_loose_object(const unsigned char *sha1,
        unsigned long size;
        int eaten;
 
-       if (read_loose_object(path, sha1, &type, &size, &contents) < 0)
+       if (read_loose_object(path, oid->hash, &type, &size, &contents) < 0)
                return NULL;
 
        if (!contents && type != OBJ_BLOB)
                die("BUG: read_loose_object streamed a non-blob");
 
-       obj = parse_object_buffer(sha1, type, size, contents, &eaten);
+       obj = parse_object_buffer(oid->hash, type, size, contents, &eaten);
 
        if (!eaten)
                free(contents);
        return obj;
 }
 
-static int fsck_loose(const unsigned char *sha1, const char *path, void *data)
+static int fsck_loose(const struct object_id *oid, const char *path, void *data)
 {
-       struct object *obj = parse_loose_object(sha1, path);
+       struct object *obj = parse_loose_object(oid, path);
 
        if (!obj) {
                errors_found |= ERROR_OBJECT;
                error("%s: object corrupt or missing: %s",
-                     sha1_to_hex(sha1), path);
+                     oid_to_hex(oid), path);
                return 0; /* keep checking other objects */
        }
 
@@ -619,26 +619,26 @@ static int fsck_cache_tree(struct cache_tree *it)
        return err;
 }
 
-static void mark_object_for_connectivity(const unsigned char *sha1)
+static void mark_object_for_connectivity(const struct object_id *oid)
 {
-       struct object *obj = lookup_unknown_object(sha1);
+       struct object *obj = lookup_unknown_object(oid->hash);
        obj->flags |= HAS_OBJ;
 }
 
-static int mark_loose_for_connectivity(const unsigned char *sha1,
+static int mark_loose_for_connectivity(const struct object_id *oid,
                                       const char *path,
                                       void *data)
 {
-       mark_object_for_connectivity(sha1);
+       mark_object_for_connectivity(oid);
        return 0;
 }
 
-static int mark_packed_for_connectivity(const unsigned char *sha1,
+static int mark_packed_for_connectivity(const struct object_id *oid,
                                        struct packed_git *pack,
                                        uint32_t pos,
                                        void *data)
 {
-       mark_object_for_connectivity(sha1);
+       mark_object_for_connectivity(oid);
        return 0;
 }
 
@@ -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 a2b9e8924e1a3e3a537d86c4a3da1fa1967c1863..91f7696a85ec974d0ff5a1591cc404ed99efb7d7 100644 (file)
@@ -64,17 +64,6 @@ static void report_pack_garbage(unsigned seen_bits, const char *path)
                string_list_append(&pack_garbage, path);
 }
 
-static void git_config_date_string(const char *key, const char **output)
-{
-       if (git_config_get_string_const(key, output))
-               return;
-       if (strcmp(*output, "now")) {
-               unsigned long now = approxidate("now");
-               if (approxidate(*output) >= now)
-                       git_die_config(key, _("Invalid %s: '%s'"), key, *output);
-       }
-}
-
 static void process_log_file(void)
 {
        struct stat st;
@@ -131,9 +120,9 @@ static void gc_config(void)
        git_config_get_int("gc.auto", &gc_auto_threshold);
        git_config_get_int("gc.autopacklimit", &gc_auto_pack_limit);
        git_config_get_bool("gc.autodetach", &detach_auto);
-       git_config_date_string("gc.pruneexpire", &prune_expire);
-       git_config_date_string("gc.worktreepruneexpire", &prune_worktrees_expire);
-       git_config_date_string("gc.logexpiry", &gc_log_expire);
+       git_config_get_expiry("gc.pruneexpire", &prune_expire);
+       git_config_get_expiry("gc.worktreepruneexpire", &prune_worktrees_expire);
+       git_config_get_expiry("gc.logexpiry", &gc_log_expire);
 
        git_config(git_default_config, NULL);
 }
@@ -146,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;
@@ -157,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;
 
@@ -249,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;
@@ -261,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 =
@@ -285,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 9304c33e75051baa65ee02d1daa03c361e25339e..3ffb5b4e8176bbcd1ecf2e4ae2dc84aee968cd22 100644 (file)
@@ -294,26 +294,23 @@ static int grep_cmd_config(const char *var, const char *value, void *cb)
        return st;
 }
 
-static void *lock_and_read_sha1_file(const unsigned char *sha1, enum object_type *type, unsigned long *size)
+static void *lock_and_read_oid_file(const struct object_id *oid, enum object_type *type, unsigned long *size)
 {
        void *data;
 
        grep_read_lock();
-       data = read_sha1_file(sha1, type, size);
+       data = read_sha1_file(oid->hash, type, size);
        grep_read_unlock();
        return data;
 }
 
-static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
+static int grep_oid(struct grep_opt *opt, const struct object_id *oid,
                     const char *filename, int tree_name_len,
                     const char *path)
 {
        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,9 +318,16 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
                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, sha1);
+               add_work(opt, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                return 0;
        } else
@@ -332,7 +336,7 @@ static int grep_sha1(struct grep_opt *opt, const unsigned char *sha1,
                struct grep_source gs;
                int hit;
 
-               grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, sha1);
+               grep_source_init(&gs, GREP_SOURCE_SHA1, pathbuf.buf, path, oid);
                strbuf_release(&pathbuf);
                hit = grep_source(opt, &gs);
 
@@ -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;
        }
 }
@@ -690,7 +696,7 @@ static int grep_cache(struct grep_opt *opt, const struct pathspec *pathspec,
                            ce_skip_worktree(ce)) {
                                if (ce_stage(ce) || ce_intent_to_add(ce))
                                        continue;
-                               hit |= grep_sha1(opt, ce->oid.hash, ce->name,
+                               hit |= grep_oid(opt, &ce->oid, ce->name,
                                                 0, ce->name);
                        } else {
                                hit |= grep_file(opt, ce->name);
@@ -750,7 +756,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                strbuf_add(base, entry.path, te_len);
 
                if (S_ISREG(entry.mode)) {
-                       hit |= grep_sha1(opt, entry.oid->hash, base->buf, tn_len,
+                       hit |= grep_oid(opt, entry.oid, base->buf, tn_len,
                                         check_attr ? base->buf + tn_len : NULL);
                } else if (S_ISDIR(entry.mode)) {
                        enum object_type type;
@@ -758,7 +764,7 @@ static int grep_tree(struct grep_opt *opt, const struct pathspec *pathspec,
                        void *data;
                        unsigned long size;
 
-                       data = lock_and_read_sha1_file(entry.oid->hash, &type, &size);
+                       data = lock_and_read_oid_file(entry.oid, &type, &size);
                        if (!data)
                                die(_("unable to read tree (%s)"),
                                    oid_to_hex(entry.oid));
@@ -787,7 +793,7 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                       struct object *obj, const char *name, const char *path)
 {
        if (obj->type == OBJ_BLOB)
-               return grep_sha1(opt, obj->oid.hash, name, 0, path);
+               return grep_oid(opt, &obj->oid, name, 0, path);
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
@@ -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")),
@@ -1169,7 +1175,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
        allow_revs = use_index && !untracked;
        for (i = 0; i < argc; i++) {
                const char *arg = argv[i];
-               unsigned char sha1[20];
+               struct object_id oid;
                struct object_context oc;
                struct object *object;
 
@@ -1184,13 +1190,13 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        break;
                }
 
-               if (get_sha1_with_context(arg, 0, sha1, &oc)) {
+               if (get_sha1_with_context(arg, 0, oid.hash, &oc)) {
                        if (seen_dashdash)
                                die(_("unable to resolve revision: %s"), arg);
                        break;
                }
 
-               object = parse_object_or_die(sha1, arg);
+               object = parse_object_or_die(oid.hash, arg);
                if (!seen_dashdash)
                        verify_non_filename(prefix, arg);
                add_object_array_with_path(object, arg, &list, oc.mode, oc.path);
@@ -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 88d205f858e93f6d7c8fca17803fc6fadee47750..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);
@@ -1442,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
index 55d20cc2d88ab03d96f0476b222be3b2f6832ed9..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);
@@ -989,8 +996,7 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
            open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
                return;
 
-       log_write_email_headers(rev, head, &pp.subject, &pp.after_subject,
-                               &need_8bit_cte);
+       log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte);
 
        for (i = 0; !need_8bit_cte && i < nr; i++) {
                const char *buf = get_commit_buffer(list[i], NULL);
@@ -1005,6 +1011,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
        msg = body;
        pp.fmt = CMIT_FMT_EMAIL;
        pp.date_mode.type = DATE_RFC2822;
+       pp.rev = rev;
+       pp.print_email_subject = 1;
        pp_user_info(&pp, NULL, &sb, committer, encoding);
        pp_title_line(&pp, &msg, &sb, encoding, need_8bit_cte);
        pp_remainder(&pp, &msg, &sb, 0);
@@ -1083,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 b572a37c2611b2a8c20b1db7edac68623d371a38..cfe2a796f85ecf8dfbbb35d3232893929670b492 100644 (file)
@@ -36,12 +36,12 @@ static const char * const merge_base_usage[] = {
 
 static struct commit *get_commit_reference(const char *arg)
 {
-       unsigned char revkey[20];
+       struct object_id revkey;
        struct commit *r;
 
-       if (get_sha1(arg, revkey))
+       if (get_oid(arg, &revkey))
                die("Not a valid object name %s", arg);
-       r = lookup_commit_reference(revkey);
+       r = lookup_commit_reference(revkey.hash);
        if (!r)
                die("Not a valid commit name %s", arg);
 
@@ -113,14 +113,14 @@ struct rev_collect {
        unsigned int initial : 1;
 };
 
-static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
+static void add_one_commit(struct object_id *oid, struct rev_collect *revs)
 {
        struct commit *commit;
 
-       if (is_null_sha1(sha1))
+       if (is_null_oid(oid))
                return;
 
-       commit = lookup_commit(sha1);
+       commit = lookup_commit(oid->hash);
        if (!commit ||
            (commit->object.flags & TMP_MARK) ||
            parse_commit(commit))
@@ -131,7 +131,7 @@ static void add_one_commit(unsigned char *sha1, struct rev_collect *revs)
        commit->object.flags |= TMP_MARK;
 }
 
-static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int collect_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
                                  const char *ident, unsigned long timestamp,
                                  int tz, const char *message, void *cbdata)
 {
@@ -139,15 +139,15 @@ static int collect_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
 
        if (revs->initial) {
                revs->initial = 0;
-               add_one_commit(osha1, revs);
+               add_one_commit(ooid, revs);
        }
-       add_one_commit(nsha1, revs);
+       add_one_commit(noid, revs);
        return 0;
 }
 
 static int handle_fork_point(int argc, const char **argv)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        char *refname;
        const char *commitname;
        struct rev_collect revs;
@@ -155,7 +155,7 @@ static int handle_fork_point(int argc, const char **argv)
        struct commit_list *bases;
        int i, ret = 0;
 
-       switch (dwim_ref(argv[0], strlen(argv[0]), sha1, &refname)) {
+       switch (dwim_ref(argv[0], strlen(argv[0]), oid.hash, &refname)) {
        case 0:
                die("No such ref: '%s'", argv[0]);
        case 1:
@@ -165,16 +165,16 @@ static int handle_fork_point(int argc, const char **argv)
        }
 
        commitname = (argc == 2) ? argv[1] : "HEAD";
-       if (get_sha1(commitname, sha1))
+       if (get_oid(commitname, &oid))
                die("Not a valid object name: '%s'", commitname);
 
-       derived = lookup_commit_reference(sha1);
+       derived = lookup_commit_reference(oid.hash);
        memset(&revs, 0, sizeof(revs));
        revs.initial = 1;
        for_each_reflog_ent(refname, collect_one_reflog_ent, &revs);
 
-       if (!revs.nr && !get_sha1(refname, sha1))
-               add_one_commit(sha1, &revs);
+       if (!revs.nr && !get_oid(refname, &oid))
+               add_one_commit(&oid, &revs);
 
        for (i = 0; i < revs.nr; i++)
                revs.commit[i]->object.flags &= ~TMP_MARK;
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 848a29855611d22bf19f3740a577a2b312294257..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
@@ -244,7 +243,7 @@ static void drop_save(void)
        unlink(git_path_merge_mode());
 }
 
-static int save_state(unsigned char *stash)
+static int save_state(struct object_id *stash)
 {
        int len;
        struct child_process cp = CHILD_PROCESS_INIT;
@@ -265,7 +264,7 @@ static int save_state(unsigned char *stash)
        else if (!len)          /* no changes */
                return -1;
        strbuf_setlen(&buffer, buffer.len-1);
-       if (get_sha1(buffer.buf, stash))
+       if (get_oid(buffer.buf, stash))
                die(_("not a valid object: %s"), buffer.buf);
        return 0;
 }
@@ -305,18 +304,18 @@ static void reset_hard(unsigned const char *sha1, int verbose)
                die(_("read-tree failed"));
 }
 
-static void restore_state(const unsigned char *head,
-                         const unsigned char *stash)
+static void restore_state(const struct object_id *head,
+                         const struct object_id *stash)
 {
        struct strbuf sb = STRBUF_INIT;
        const char *args[] = { "stash", "apply", NULL, NULL };
 
-       if (is_null_sha1(stash))
+       if (is_null_oid(stash))
                return;
 
-       reset_hard(head, 1);
+       reset_hard(head->hash, 1);
 
-       args[2] = sha1_to_hex(stash);
+       args[2] = oid_to_hex(stash);
 
        /*
         * It is OK to ignore error here, for example when there was
@@ -376,10 +375,10 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 
 static void finish(struct commit *head_commit,
                   struct commit_list *remoteheads,
-                  const unsigned char *new_head, const char *msg)
+                  const struct object_id *new_head, const char *msg)
 {
        struct strbuf reflog_message = STRBUF_INIT;
-       const unsigned char *head = head_commit->object.oid.hash;
+       const struct object_id *head = &head_commit->object.oid;
 
        if (!msg)
                strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
@@ -397,7 +396,7 @@ static void finish(struct commit *head_commit,
                else {
                        const char *argv_gc_auto[] = { "gc", "--auto", NULL };
                        update_ref(reflog_message.buf, "HEAD",
-                               new_head, head, 0,
+                               new_head->hash, head->hash, 0,
                                UPDATE_REFS_DIE_ON_ERR);
                        /*
                         * We ignore errors in 'gc --auto', since the
@@ -416,7 +415,7 @@ static void finish(struct commit *head_commit,
                        DIFF_FORMAT_SUMMARY | DIFF_FORMAT_DIFFSTAT;
                opts.detect_rename = DIFF_DETECT_RENAME;
                diff_setup_done(&opts);
-               diff_tree_sha1(head, new_head, "", &opts);
+               diff_tree_sha1(head->hash, new_head->hash, "", &opts);
                diffcore_std(&opts);
                diff_flush(&opts);
        }
@@ -431,7 +430,7 @@ static void finish(struct commit *head_commit,
 static void merge_name(const char *remote, struct strbuf *msg)
 {
        struct commit *remote_head;
-       unsigned char branch_head[20];
+       struct object_id branch_head;
        struct strbuf buf = STRBUF_INIT;
        struct strbuf bname = STRBUF_INIT;
        const char *ptr;
@@ -441,25 +440,25 @@ static void merge_name(const char *remote, struct strbuf *msg)
        strbuf_branchname(&bname, remote, 0);
        remote = bname.buf;
 
-       memset(branch_head, 0, sizeof(branch_head));
+       oidclr(&branch_head);
        remote_head = get_merge_parent(remote);
        if (!remote_head)
                die(_("'%s' does not point to a commit"), remote);
 
-       if (dwim_ref(remote, strlen(remote), branch_head, &found_ref) > 0) {
+       if (dwim_ref(remote, strlen(remote), branch_head.hash, &found_ref) > 0) {
                if (starts_with(found_ref, "refs/heads/")) {
                        strbuf_addf(msg, "%s\t\tbranch '%s' of .\n",
-                                   sha1_to_hex(branch_head), remote);
+                                   oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
                if (starts_with(found_ref, "refs/tags/")) {
                        strbuf_addf(msg, "%s\t\ttag '%s' of .\n",
-                                   sha1_to_hex(branch_head), remote);
+                                   oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
                if (starts_with(found_ref, "refs/remotes/")) {
                        strbuf_addf(msg, "%s\t\tremote-tracking branch '%s' of .\n",
-                                   sha1_to_hex(branch_head), remote);
+                                   oid_to_hex(&branch_head), remote);
                        goto cleanup;
                }
        }
@@ -590,8 +589,8 @@ static int git_merge_config(const char *k, const char *v, void *cb)
        return git_diff_ui_config(k, v, cb);
 }
 
-static int read_tree_trivial(unsigned char *common, unsigned char *head,
-                            unsigned char *one)
+static int read_tree_trivial(struct object_id *common, struct object_id *head,
+                            struct object_id *one)
 {
        int i, nr_trees = 0;
        struct tree *trees[MAX_UNPACK_TREES];
@@ -606,13 +605,13 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,
        opts.verbose_update = 1;
        opts.trivial_merges_only = 1;
        opts.merge = 1;
-       trees[nr_trees] = parse_tree_indirect(common);
+       trees[nr_trees] = parse_tree_indirect(common->hash);
        if (!trees[nr_trees++])
                return -1;
-       trees[nr_trees] = parse_tree_indirect(head);
+       trees[nr_trees] = parse_tree_indirect(head->hash);
        if (!trees[nr_trees++])
                return -1;
-       trees[nr_trees] = parse_tree_indirect(one);
+       trees[nr_trees] = parse_tree_indirect(one->hash);
        if (!trees[nr_trees++])
                return -1;
        opts.fn = threeway_merge;
@@ -626,17 +625,18 @@ static int read_tree_trivial(unsigned char *common, unsigned char *head,
        return 0;
 }
 
-static void write_tree_trivial(unsigned char *sha1)
+static void write_tree_trivial(struct object_id *oid)
 {
-       if (write_cache_as_tree(sha1, 0, NULL))
+       if (write_cache_as_tree(oid->hash, 0, NULL))
                die(_("git write-tree failed to write a tree"));
 }
 
 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);
@@ -781,7 +781,7 @@ static void prepare_to_commit(struct commit_list *remoteheads)
 
 static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
 {
-       unsigned char result_tree[20], result_commit[20];
+       struct object_id result_tree, result_commit;
        struct commit_list *parents, **pptr = &parents;
        static struct lock_file lock;
 
@@ -792,15 +792,15 @@ static int merge_trivial(struct commit *head, struct commit_list *remoteheads)
                return error(_("Unable to write index."));
        rollback_lock_file(&lock);
 
-       write_tree_trivial(result_tree);
+       write_tree_trivial(&result_tree);
        printf(_("Wonderful.\n"));
        pptr = commit_list_append(head, pptr);
        pptr = commit_list_append(remoteheads->item, pptr);
        prepare_to_commit(remoteheads);
-       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
-                       result_commit, NULL, sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree.hash, parents,
+                       result_commit.hash, NULL, sign_commit))
                die(_("failed to write commit object"));
-       finish(head, remoteheads, result_commit, "In-index merge");
+       finish(head, remoteheads, &result_commit, "In-index merge");
        drop_save();
        return 0;
 }
@@ -809,12 +809,12 @@ static int finish_automerge(struct commit *head,
                            int head_subsumed,
                            struct commit_list *common,
                            struct commit_list *remoteheads,
-                           unsigned char *result_tree,
+                           struct object_id *result_tree,
                            const char *wt_strategy)
 {
        struct commit_list *parents = NULL;
        struct strbuf buf = STRBUF_INIT;
-       unsigned char result_commit[20];
+       struct object_id result_commit;
 
        free_commit_list(common);
        parents = remoteheads;
@@ -822,11 +822,11 @@ static int finish_automerge(struct commit *head,
                commit_list_insert(head, &parents);
        strbuf_addch(&merge_msg, '\n');
        prepare_to_commit(remoteheads);
-       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree, parents,
-                       result_commit, NULL, sign_commit))
+       if (commit_tree(merge_msg.buf, merge_msg.len, result_tree->hash, parents,
+                       result_commit.hash, NULL, sign_commit))
                die(_("failed to write commit object"));
        strbuf_addf(&buf, "Merge made by the '%s' strategy.", wt_strategy);
-       finish(head, remoteheads, result_commit, buf.buf);
+       finish(head, remoteheads, &result_commit, buf.buf);
        strbuf_release(&buf);
        drop_save();
        return 0;
@@ -853,24 +853,6 @@ static int suggest_conflicts(void)
        return 1;
 }
 
-static struct commit *is_old_style_invocation(int argc, const char **argv,
-                                             const unsigned char *head)
-{
-       struct commit *second_token = NULL;
-       if (argc > 2) {
-               unsigned char second_sha1[20];
-
-               if (get_sha1(argv[1], second_sha1))
-                       return NULL;
-               second_token = lookup_commit_reference_gently(second_sha1, 0);
-               if (!second_token)
-                       die(_("'%s' is not a commit"), argv[1]);
-               if (hashcmp(second_token->object.oid.hash, head))
-                       return NULL;
-       }
-       return second_token;
-}
-
 static int evaluate_result(void)
 {
        int cnt = 0;
@@ -1038,7 +1020,7 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
                die_errno(_("could not close '%s'"), filename);
 
        for (pos = 0; pos < merge_names->len; pos = npos) {
-               unsigned char sha1[20];
+               struct object_id oid;
                char *ptr;
                struct commit *commit;
 
@@ -1048,16 +1030,16 @@ static void handle_fetch_head(struct commit_list **remotes, struct strbuf *merge
                else
                        npos = merge_names->len;
 
-               if (npos - pos < 40 + 2 ||
-                   get_sha1_hex(merge_names->buf + pos, sha1))
+               if (npos - pos < GIT_SHA1_HEXSZ + 2 ||
+                   get_oid_hex(merge_names->buf + pos, &oid))
                        commit = NULL; /* bad */
-               else if (memcmp(merge_names->buf + pos + 40, "\t\t", 2))
+               else if (memcmp(merge_names->buf + pos + GIT_SHA1_HEXSZ, "\t\t", 2))
                        continue; /* not-for-merge */
                else {
-                       char saved = merge_names->buf[pos + 40];
-                       merge_names->buf[pos + 40] = '\0';
+                       char saved = merge_names->buf[pos + GIT_SHA1_HEXSZ];
+                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = '\0';
                        commit = get_merge_parent(merge_names->buf + pos);
-                       merge_names->buf[pos + 40] = saved;
+                       merge_names->buf[pos + GIT_SHA1_HEXSZ] = saved;
                }
                if (!commit) {
                        if (ptr)
@@ -1117,12 +1099,9 @@ static struct commit_list *collect_parents(struct commit *head_commit,
 
 int cmd_merge(int argc, const char **argv, const char *prefix)
 {
-       unsigned char result_tree[20];
-       unsigned char stash[20];
-       unsigned char head_sha1[20];
+       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;
@@ -1138,13 +1117,13 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * Check if we are _not_ on a detached HEAD, i.e. if there is a
         * current branch.
         */
-       branch = branch_to_free = resolve_refdup("HEAD", 0, head_sha1, NULL);
+       branch = branch_to_free = resolve_refdup("HEAD", 0, head_oid.hash, NULL);
        if (branch && starts_with(branch, "refs/heads/"))
                branch += 11;
-       if (!branch || is_null_sha1(head_sha1))
+       if (!branch || is_null_oid(&head_oid))
                head_commit = NULL;
        else
-               head_commit = lookup_commit_or_die(head_sha1, "HEAD");
+               head_commit = lookup_commit_or_die(head_oid.hash, "HEAD");
 
        init_diff_ui_defaults();
        git_config(git_merge_config, NULL);
@@ -1242,7 +1221,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * to forbid "git merge" into a branch yet to be born.
                 * We do the same for "git pull".
                 */
-               unsigned char *remote_head_sha1;
+               struct object_id *remote_head_oid;
                if (squash)
                        die(_("Squash commit into empty head not supported yet"));
                if (fast_forward == FF_NO)
@@ -1254,42 +1233,20 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        die(_("%s - not something we can merge"), argv[0]);
                if (remoteheads->next)
                        die(_("Can merge only exactly one commit into empty head"));
-               remote_head_sha1 = remoteheads->item->object.oid.hash;
-               read_empty(remote_head_sha1, 0);
-               update_ref("initial pull", "HEAD", remote_head_sha1,
+               remote_head_oid = &remoteheads->item->object.oid;
+               read_empty(remote_head_oid->hash, 0);
+               update_ref("initial pull", "HEAD", remote_head_oid->hash,
                           NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
        }
 
        /*
-        * 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.hash)) {
-               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,
@@ -1298,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));
 
@@ -1422,7 +1379,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               finish(head_commit, remoteheads, commit->object.oid.hash, msg.buf);
+               finish(head_commit, remoteheads, &commit->object.oid, msg.buf);
                drop_save();
                goto done;
        } else if (!remoteheads->next && common->next)
@@ -1441,9 +1398,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_STRICT);
                        printf(_("Trying really trivial in-index merge...\n"));
-                       if (!read_tree_trivial(common->item->object.oid.hash,
-                                              head_commit->object.oid.hash,
-                                              remoteheads->item->object.oid.hash)) {
+                       if (!read_tree_trivial(&common->item->object.oid,
+                                              &head_commit->object.oid,
+                                              &remoteheads->item->object.oid)) {
                                ret = merge_trivial(head_commit, remoteheads);
                                goto done;
                        }
@@ -1495,14 +1452,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
            /*
             * Stash away the local changes so that we can try more than one.
             */
-           save_state(stash))
-               hashclr(stash);
+           save_state(&stash))
+               oidclr(&stash);
 
        for (i = 0; i < use_strategies_nr; i++) {
                int ret;
                if (i) {
                        printf(_("Rewinding the tree to pristine...\n"));
-                       restore_state(head_commit->object.oid.hash, stash);
+                       restore_state(&head_commit->object.oid, &stash);
                }
                if (use_strategies_nr != 1)
                        printf(_("Trying merge strategy %s...\n"),
@@ -1515,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;
                        /*
@@ -1547,7 +1504,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                }
 
                /* Automerge succeeded. */
-               write_tree_trivial(result_tree);
+               write_tree_trivial(&result_tree);
                automerge_was_ok = 1;
                break;
        }
@@ -1559,7 +1516,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        if (automerge_was_ok) {
                ret = finish_automerge(head_commit, head_subsumed,
                                       common, remoteheads,
-                                      result_tree, wt_strategy);
+                                      &result_tree, wt_strategy);
                goto done;
        }
 
@@ -1568,7 +1525,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * it up.
         */
        if (!best_strategy) {
-               restore_state(head_commit->object.oid.hash, stash);
+               restore_state(&head_commit->object.oid, &stash);
                if (use_strategies_nr > 1)
                        fprintf(stderr,
                                _("No merge strategy handled the merge.\n"));
@@ -1581,11 +1538,11 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                ; /* We already have its result in the working tree. */
        else {
                printf(_("Rewinding the tree to pristine...\n"));
-               restore_state(head_commit->object.oid.hash, stash);
+               restore_state(&head_commit->object.oid, &stash);
                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 cd89d48b65e8f70819f73ef6e2cfd0634752b284..92a5d8a5d263f78afaa2f426135998348dae53b8 100644 (file)
@@ -108,7 +108,8 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
 struct name_ref_data {
        int tags_only;
        int name_only;
-       const char *ref_filter;
+       struct string_list ref_filters;
+       struct string_list exclude_filters;
 };
 
 static struct tip_table {
@@ -150,18 +151,49 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
        if (data->tags_only && !starts_with(path, "refs/tags/"))
                return 0;
 
-       if (data->ref_filter) {
-               switch (subpath_matches(path, data->ref_filter)) {
-               case -1: /* did not match */
-                       return 0;
-               case 0:  /* matched fully */
-                       break;
-               default: /* matched subpath */
-                       can_abbreviate_output = 1;
-                       break;
+       if (data->exclude_filters.nr) {
+               struct string_list_item *item;
+
+               for_each_string_list_item(item, &data->exclude_filters) {
+                       if (subpath_matches(path, item->string) >= 0)
+                               return 0;
                }
        }
 
+       if (data->ref_filters.nr) {
+               struct string_list_item *item;
+               int matched = 0;
+
+               /* See if any of the patterns match. */
+               for_each_string_list_item(item, &data->ref_filters) {
+                       /*
+                        * Check all patterns even after finding a match, so
+                        * that we can see if a match with a subpath exists.
+                        * When a user asked for 'refs/tags/v*' and 'v1.*',
+                        * both of which match, the user is showing her
+                        * willingness to accept a shortened output by having
+                        * the 'v1.*' in the acceptable refnames, so we
+                        * shouldn't stop when seeing 'refs/tags/v1.4' matches
+                        * 'refs/tags/v*'.  We should show it as 'v1.4'.
+                        */
+                       switch (subpath_matches(path, item->string)) {
+                       case -1: /* did not match */
+                               break;
+                       case 0: /* matched fully */
+                               matched = 1;
+                               break;
+                       default: /* matched subpath */
+                               matched = 1;
+                               can_abbreviate_output = 1;
+                               break;
+                       }
+               }
+
+               /* If none of the patterns matched, stop now */
+               if (!matched)
+                       return 0;
+       }
+
        add_to_tip_table(oid->hash, path, can_abbreviate_output);
 
        while (o && o->type == OBJ_TAG) {
@@ -206,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;
 
@@ -226,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;
        }
 }
 
@@ -239,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)
@@ -251,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[] = {
@@ -262,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++) {
@@ -282,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;
 
@@ -300,18 +333,22 @@ 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)
 {
        struct object_array revs = OBJECT_ARRAY_INIT;
        int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
-       struct name_ref_data data = { 0, 0, NULL };
+       struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP, STRING_LIST_INIT_NODUP };
        struct option opts[] = {
                OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
                OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
-               OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"),
+               OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
                                   N_("only use refs matching <pattern>")),
+               OPT_STRING_LIST(0, "exclude", &data.exclude_filters, N_("pattern"),
+                                  N_("ignore refs matching <pattern>")),
                OPT_GROUP(""),
                OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),
                OPT_BOOL(0, "stdin", &transform_stdin, N_("read from stdin")),
index 5248a9bad8d4d18c8ff0f145e8049606baa8849a..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;
@@ -681,9 +680,9 @@ static int merge_abort(struct notes_merge_options *o)
         * notes_merge_abort() to remove .git/NOTES_MERGE_WORKTREE.
         */
 
-       if (delete_ref("NOTES_MERGE_PARTIAL", NULL, 0))
+       if (delete_ref(NULL, "NOTES_MERGE_PARTIAL", NULL, 0))
                ret += error(_("failed to delete ref NOTES_MERGE_PARTIAL"));
-       if (delete_ref("NOTES_MERGE_REF", NULL, REF_NODEREF))
+       if (delete_ref(NULL, "NOTES_MERGE_REF", NULL, REF_NODEREF))
                ret += error(_("failed to delete ref NOTES_MERGE_REF"));
        if (notes_merge_abort(o))
                ret += error(_("failed to remove 'git notes merge' worktree"));
@@ -693,7 +692,7 @@ static int merge_abort(struct notes_merge_options *o)
 static int merge_commit(struct notes_merge_options *o)
 {
        struct strbuf msg = STRBUF_INIT;
-       unsigned char sha1[20], parent_sha1[20];
+       struct object_id oid, parent_oid;
        struct notes_tree *t;
        struct commit *partial;
        struct pretty_print_context pretty_ctx;
@@ -705,27 +704,27 @@ static int merge_commit(struct notes_merge_options *o)
         * and target notes ref from .git/NOTES_MERGE_REF.
         */
 
-       if (get_sha1("NOTES_MERGE_PARTIAL", sha1))
+       if (get_oid("NOTES_MERGE_PARTIAL", &oid))
                die(_("failed to read ref NOTES_MERGE_PARTIAL"));
-       else if (!(partial = lookup_commit_reference(sha1)))
+       else if (!(partial = lookup_commit_reference(oid.hash)))
                die(_("could not find commit from NOTES_MERGE_PARTIAL."));
        else if (parse_commit(partial))
                die(_("could not parse commit from NOTES_MERGE_PARTIAL."));
 
        if (partial->parents)
-               hashcpy(parent_sha1, partial->parents->item->object.oid.hash);
+               oidcpy(&parent_oid, &partial->parents->item->object.oid);
        else
-               hashclr(parent_sha1);
+               oidclr(&parent_oid);
 
        t = xcalloc(1, sizeof(struct notes_tree));
        init_notes(t, "NOTES_MERGE_PARTIAL", combine_notes_overwrite, 0);
 
        o->local_ref = local_ref_to_free =
-               resolve_refdup("NOTES_MERGE_REF", 0, sha1, NULL);
+               resolve_refdup("NOTES_MERGE_REF", 0, oid.hash, NULL);
        if (!o->local_ref)
                die(_("failed to resolve NOTES_MERGE_REF"));
 
-       if (notes_merge_commit(o, t, partial, sha1))
+       if (notes_merge_commit(o, t, partial, oid.hash))
                die(_("failed to finalize notes merge"));
 
        /* Reuse existing commit message in reflog message */
@@ -733,8 +732,8 @@ static int merge_commit(struct notes_merge_options *o)
        format_commit_message(partial, "%s", &msg, &pretty_ctx);
        strbuf_trim(&msg);
        strbuf_insert(&msg, 0, "notes: ", 7);
-       update_ref(msg.buf, o->local_ref, sha1,
-                  is_null_sha1(parent_sha1) ? NULL : parent_sha1,
+       update_ref(msg.buf, o->local_ref, oid.hash,
+                  is_null_oid(&parent_oid) ? NULL : parent_oid.hash,
                   0, UPDATE_REFS_DIE_ON_ERR);
 
        free_notes(t);
index c7af4754857f373df37d2f15dcd6c4e4384ec570..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);
@@ -894,24 +898,15 @@ static void write_pack_file(void)
                        written, nr_result);
 }
 
-static void setup_delta_attr_check(struct git_attr_check *check)
-{
-       static struct git_attr *attr_delta;
-
-       if (!attr_delta)
-               attr_delta = git_attr("delta");
-
-       check[0].attr = attr_delta;
-}
-
 static int no_try_delta(const char *path)
 {
-       struct git_attr_check check[1];
+       static struct attr_check *check;
 
-       setup_delta_attr_check(check);
-       if (git_check_attr(path, ARRAY_SIZE(check), check))
+       if (!check)
+               check = attr_check_initl("delta", NULL);
+       if (git_check_attr(path, check))
                return 0;
-       if (ATTR_FALSE(check->value))
+       if (ATTR_FALSE(check->items[0].value))
                return 1;
        return 0;
 }
@@ -2621,17 +2616,17 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
        free(in_pack.array);
 }
 
-static int add_loose_object(const unsigned char *sha1, const char *path,
+static int add_loose_object(const struct object_id *oid, const char *path,
                            void *data)
 {
-       enum object_type type = sha1_object_info(sha1, NULL);
+       enum object_type type = sha1_object_info(oid->hash, NULL);
 
        if (type < 0) {
                warning("loose object at %s could not be examined", path);
                return 0;
        }
 
-       add_object_entry(sha1, type, "", 0);
+       add_object_entry(oid->hash, type, "", 0);
        return 0;
 }
 
@@ -2677,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;
 }
@@ -2695,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)
@@ -2705,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");
                }
        }
@@ -2748,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)
@@ -2821,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 7cf900ea0765e334f02df5600f0911d41563a027..c026299e789abe19826264225fa2000b3149f875 100644 (file)
@@ -19,12 +19,12 @@ static int prune_subdir(int nr, const char *path, void *data)
        return 0;
 }
 
-static int prune_object(const unsigned char *sha1, const char *path,
+static int prune_object(const struct object_id *oid, const char *path,
                         void *data)
 {
        int *opts = data;
 
-       if (!has_sha1_pack(sha1))
+       if (!has_sha1_pack(oid->hash))
                return 0;
 
        if (*opts & PRUNE_PACKED_DRY_RUN)
index 8f4f0522856b988a8798fd65c6d81d8826709aa2..42633e0c6e672c46852f0590f1941249691f962c 100644 (file)
@@ -30,7 +30,7 @@ static int prune_tmp_file(const char *fullpath)
        return 0;
 }
 
-static int prune_object(const unsigned char *sha1, const char *fullpath,
+static int prune_object(const struct object_id *oid, const char *fullpath,
                        void *data)
 {
        struct stat st;
@@ -39,7 +39,7 @@ static int prune_object(const unsigned char *sha1, const char *fullpath,
         * Do we know about this object?
         * It must have been reachable
         */
-       if (lookup_object(sha1))
+       if (lookup_object(oid->hash))
                return 0;
 
        if (lstat(fullpath, &st)) {
@@ -50,8 +50,8 @@ static int prune_object(const unsigned char *sha1, const char *fullpath,
        if (st.st_mtime > expire)
                return 0;
        if (show_only || verbose) {
-               enum object_type type = sha1_object_info(sha1, NULL);
-               printf("%s %s\n", sha1_to_hex(sha1),
+               enum object_type type = sha1_object_info(oid->hash, NULL);
+               printf("%s %s\n", oid_to_hex(oid),
                       (type > 0) ? typename(type) : "unknown");
        }
        if (!show_only)
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?");
diff --git a/builtin/rebase--helper.c b/builtin/rebase--helper.c
new file mode 100644 (file)
index 0000000..ca1ebb2
--- /dev/null
@@ -0,0 +1,40 @@
+#include "builtin.h"
+#include "cache.h"
+#include "parse-options.h"
+#include "sequencer.h"
+
+static const char * const builtin_rebase_helper_usage[] = {
+       N_("git rebase--helper [<options>]"),
+       NULL
+};
+
+int cmd_rebase__helper(int argc, const char **argv, const char *prefix)
+{
+       struct replay_opts opts = REPLAY_OPTS_INIT;
+       enum {
+               CONTINUE = 1, ABORT
+       } command = 0;
+       struct option options[] = {
+               OPT_BOOL(0, "ff", &opts.allow_ff, N_("allow fast-forward")),
+               OPT_CMDMODE(0, "continue", &command, N_("continue rebase"),
+                               CONTINUE),
+               OPT_CMDMODE(0, "abort", &command, N_("abort rebase"),
+                               ABORT),
+               OPT_END()
+       };
+
+       git_config(git_default_config, NULL);
+
+       opts.action = REPLAY_INTERACTIVE_REBASE;
+       opts.allow_ff = 1;
+       opts.allow_empty = 1;
+
+       argc = parse_options(argc, argv, NULL, options,
+                       builtin_rebase_helper_usage, PARSE_OPT_KEEP_ARGV0);
+
+       if (command == CONTINUE && argc == 1)
+               return !!sequencer_continue(&opts);
+       if (command == ABORT && argc == 1)
+               return !!sequencer_remove_state(&opts);
+       usage_with_options(builtin_rebase_helper_usage, options);
+}
index b618d52139beccafdb77cc4eed4d3fc121b4124a..f96834f42c9849746b64c20c84869232cbac7367 100644 (file)
@@ -21,6 +21,7 @@
 #include "sigchain.h"
 #include "fsck.h"
 #include "tmp-objdir.h"
+#include "oidset.h"
 
 static const char * const receive_pack_usage[] = {
        N_("git receive-pack <git-dir>"),
@@ -224,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;
 
@@ -243,15 +244,16 @@ 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;
        }
 }
 
 static int show_ref_cb(const char *path_full, const struct object_id *oid,
-                      int flag, void *unused)
+                      int flag, void *data)
 {
+       struct oidset *seen = data;
        const char *path = strip_namespace(path_full);
 
        if (ref_is_hidden(path, path_full))
@@ -260,39 +262,40 @@ static int show_ref_cb(const char *path_full, const struct object_id *oid,
        /*
         * Advertise refs outside our current namespace as ".have"
         * refs, so that the client can use them to minimize data
-        * transfer but will otherwise ignore them. This happens to
-        * cover ".have" that are thrown in by add_one_alternate_ref()
-        * to mark histories that are complete in our alternates as
-        * well.
+        * transfer but will otherwise ignore them.
         */
-       if (!path)
+       if (!path) {
+               if (oidset_insert(seen, oid))
+                       return 0;
                path = ".have";
-       show_ref(path, oid->hash);
+       } else {
+               oidset_insert(seen, oid);
+       }
+       show_ref(path, oid);
        return 0;
 }
 
-static int show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
+static void show_one_alternate_ref(const char *refname,
+                                  const struct object_id *oid,
+                                  void *data)
 {
-       show_ref(".have", sha1);
-       return 0;
-}
+       struct oidset *seen = data;
 
-static void collect_one_alternate_ref(const struct ref *ref, void *data)
-{
-       struct sha1_array *sa = data;
-       sha1_array_append(sa, ref->old_oid.hash);
+       if (oidset_insert(seen, oid))
+               return;
+
+       show_ref(".have", oid);
 }
 
 static void write_head_info(void)
 {
-       struct sha1_array sa = SHA1_ARRAY_INIT;
+       static struct oidset seen = OIDSET_INIT;
 
-       for_each_alternate_ref(collect_one_alternate_ref, &sa);
-       sha1_array_for_each_unique(&sa, show_one_alternate_sha1, NULL);
-       sha1_array_clear(&sa);
-       for_each_ref(show_ref_cb, NULL);
+       for_each_ref(show_ref_cb, &seen);
+       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);
 
@@ -306,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 */
 };
 
@@ -720,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) {
@@ -761,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)
@@ -828,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;
@@ -839,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;
        }
 
@@ -856,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;
 }
 
@@ -985,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)) {
@@ -1011,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";
@@ -1050,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 ||
@@ -1078,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 {
@@ -1091,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);
@@ -1108,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);
@@ -1159,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);
@@ -1184,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;
@@ -1193,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";
@@ -1228,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;
 }
 
@@ -1272,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;
                }
@@ -1300,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";
@@ -1411,7 +1413,7 @@ static void execute_commands(struct command *commands,
 {
        struct check_connected_options opt = CHECK_CONNECTED_INIT;
        struct command *cmd;
-       unsigned char sha1[20];
+       struct object_id oid;
        struct iterate_data data;
        struct async muxer;
        int err_fd = 0;
@@ -1468,7 +1470,7 @@ static void execute_commands(struct command *commands,
        check_aliased_updates(commands);
 
        free(head_name_to_free);
-       head_name = head_name_to_free = resolve_refdup("HEAD", 0, sha1, NULL);
+       head_name = head_name_to_free = resolve_refdup("HEAD", 0, oid.hash, NULL);
 
        if (use_atomic)
                execute_commands_atomic(commands, si);
@@ -1483,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;
 }
@@ -1521,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;
@@ -1538,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;
                }
 
@@ -1631,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
@@ -1650,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);
@@ -1676,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)
@@ -1692,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",
@@ -1801,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;
@@ -1812,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;
@@ -1827,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";
@@ -1865,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;
@@ -1875,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[] = {
@@ -1968,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 7a7136e53e2b8cf1eaa1c79b1183692dd54278a7..747277577857af2c71ab74b83d9ab3aea863c597 100644 (file)
@@ -615,7 +615,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
        return status;
 }
 
-static int count_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int count_reflog_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
index 7682206c1e3a6e8655f59505e6e7e288ad2d3a65..addf97ad29343b2328adce2c27ccea92aee6af7a 100644 (file)
@@ -691,7 +691,7 @@ static int mv(int argc, const char **argv)
                read_ref_full(item->string, RESOLVE_REF_READING, oid.hash, &flag);
                if (!(flag & REF_ISSYMREF))
                        continue;
-               if (delete_ref(item->string, NULL, REF_NODEREF))
+               if (delete_ref(NULL, item->string, NULL, REF_NODEREF))
                        die(_("deleting '%s' failed"), item->string);
        }
        for (i = 0; i < remote_branches.nr; i++) {
@@ -1250,7 +1250,7 @@ static int set_head(int argc, const char **argv)
                        head_name = xstrdup(states.heads.items[0].string);
                free_remote_ref_states(&states);
        } else if (opt_d && !opt_a && argc == 1) {
-               if (delete_ref(buf.buf, NULL, REF_NODEREF))
+               if (delete_ref(NULL, buf.buf, NULL, REF_NODEREF))
                        result |= error(_("Could not delete %s"), buf.buf);
        } else
                usage_with_options(builtin_remote_sethead_usage, options);
index b58c714cb8935efd08c81c8ebfd8922e1704c8f4..ab17668f4330a5e688d423a60c138768b9955e1b 100644 (file)
@@ -88,78 +88,81 @@ static int list_replace_refs(const char *pattern, const char *format)
 }
 
 typedef int (*each_replace_name_fn)(const char *name, const char *ref,
-                                   const unsigned char *sha1);
+                                   const struct object_id *oid);
 
 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;
-       unsigned char sha1[20];
+       struct object_id oid;
+
+       strbuf_addstr(&ref, git_replace_ref_base);
+       base_len = ref.len;
 
        for (p = argv; *p; p++) {
-               if (get_sha1(*p, sha1)) {
+               if (get_oid(*p, &oid)) {
                        error("Failed to resolve '%s' as a valid ref.", *p);
                        had_error = 1;
                        continue;
                }
-               full_hex = sha1_to_hex(sha1);
-               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, sha1)) {
+
+               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, sha1))
+               if (fn(full_hex, ref.buf, &oid))
                        had_error = 1;
        }
+       strbuf_release(&ref);
        return had_error;
 }
 
 static int delete_replace_ref(const char *name, const char *ref,
-                             const unsigned char *sha1)
+                             const struct object_id *oid)
 {
-       if (delete_ref(ref, sha1, 0))
+       if (delete_ref(NULL, ref, oid->hash, 0))
                return 1;
        printf("Deleted replace ref '%s'\n", name);
        return 0;
 }
 
-static void check_ref_valid(unsigned char object[20],
-                           unsigned char prev[20],
-                           char *ref,
-                           int ref_size,
+static void check_ref_valid(struct object_id *object,
+                           struct object_id *prev,
+                           struct strbuf *ref,
                            int force)
 {
-       if (snprintf(ref, ref_size,
-                    "%s%s", git_replace_ref_base,
-                    sha1_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))
-               hashclr(prev);
+       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_sha1(const char *object_ref,
-                              unsigned char object[20],
+static int replace_object_oid(const char *object_ref,
+                              struct object_id *object,
                               const char *replace_ref,
-                              unsigned char repl[20],
+                              struct object_id *repl,
                               int force)
 {
-       unsigned char prev[20];
+       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;
 
-       obj_type = sha1_object_info(object, NULL);
-       repl_type = sha1_object_info(repl, NULL);
+       obj_type = sha1_object_info(object->hash, NULL);
+       repl_type = sha1_object_info(repl->hash, NULL);
        if (!force && obj_type != repl_type)
                die("Objects must be of the same type.\n"
                    "'%s' points to a replaced object of type '%s'\n"
@@ -167,29 +170,30 @@ static int replace_object_sha1(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, prev,
+           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;
 }
 
 static int replace_object(const char *object_ref, const char *replace_ref, int force)
 {
-       unsigned char object[20], repl[20];
+       struct object_id object, repl;
 
-       if (get_sha1(object_ref, object))
+       if (get_oid(object_ref, &object))
                die("Failed to resolve '%s' as a valid ref.", object_ref);
-       if (get_sha1(replace_ref, repl))
+       if (get_oid(replace_ref, &repl))
                die("Failed to resolve '%s' as a valid ref.", replace_ref);
 
-       return replace_object_sha1(object_ref, object, replace_ref, repl, force);
+       return replace_object_oid(object_ref, &object, replace_ref, &repl, force);
 }
 
 /*
@@ -197,7 +201,7 @@ static int replace_object(const char *object_ref, const char *replace_ref, int f
  * If "raw" is true, then the object's raw contents are printed according to
  * "type". Otherwise, we pretty-print the contents for human editing.
  */
-static void export_object(const unsigned char *sha1, enum object_type type,
+static void export_object(const struct object_id *oid, enum object_type type,
                          int raw, const char *filename)
 {
        struct child_process cmd = CHILD_PROCESS_INIT;
@@ -213,7 +217,7 @@ static void export_object(const unsigned char *sha1, enum object_type type,
                argv_array_push(&cmd.args, typename(type));
        else
                argv_array_push(&cmd.args, "-p");
-       argv_array_push(&cmd.args, sha1_to_hex(sha1));
+       argv_array_push(&cmd.args, oid_to_hex(oid));
        cmd.git_cmd = 1;
        cmd.out = fd;
 
@@ -226,7 +230,7 @@ static void export_object(const unsigned char *sha1, enum object_type type,
  * interpreting it as "type", and writing the result to the object database.
  * The sha1 of the written object is returned via sha1.
  */
-static void import_object(unsigned char *sha1, enum object_type type,
+static void import_object(struct object_id *oid, enum object_type type,
                          int raw, const char *filename)
 {
        int fd;
@@ -254,7 +258,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
 
                if (finish_command(&cmd))
                        die("mktree reported failure");
-               if (get_sha1_hex(result.buf, sha1) < 0)
+               if (get_oid_hex(result.buf, oid) < 0)
                        die("mktree did not return an object name");
 
                strbuf_release(&result);
@@ -264,7 +268,7 @@ static void import_object(unsigned char *sha1, enum object_type type,
 
                if (fstat(fd, &st) < 0)
                        die_errno("unable to fstat %s", filename);
-               if (index_fd(sha1, fd, &st, type, NULL, flags) < 0)
+               if (index_fd(oid->hash, fd, &st, type, NULL, flags) < 0)
                        die("unable to write object to database");
                /* index_fd close()s fd for us */
        }
@@ -279,29 +283,30 @@ static int edit_and_replace(const char *object_ref, int force, int raw)
 {
        char *tmpfile = git_pathdup("REPLACE_EDITOBJ");
        enum object_type type;
-       unsigned char old[20], new[20], prev[20];
-       char ref[PATH_MAX];
+       struct object_id old, new, prev;
+       struct strbuf ref = STRBUF_INIT;
 
-       if (get_sha1(object_ref, old) < 0)
+       if (get_oid(object_ref, &old) < 0)
                die("Not a valid object name: '%s'", object_ref);
 
-       type = sha1_object_info(old, NULL);
+       type = sha1_object_info(old.hash, NULL);
        if (type < 0)
-               die("unable to get object type for %s", sha1_to_hex(old));
+               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);
+       export_object(&old, type, raw, tmpfile);
        if (launch_editor(tmpfile, NULL, NULL) < 0)
                die("editing object file failed");
-       import_object(new, type, raw, tmpfile);
+       import_object(&new, type, raw, tmpfile);
 
        free(tmpfile);
 
-       if (!hashcmp(old, new))
-               return error("new object is the same as the old one: '%s'", sha1_to_hex(old));
+       if (!oidcmp(&old, &new))
+               return error("new object is the same as the old one: '%s'", oid_to_hex(&old));
 
-       return replace_object_sha1(object_ref, old, "replacement", new, force);
+       return replace_object_oid(object_ref, &old, "replacement", &new, force);
 }
 
 static void replace_parents(struct strbuf *buf, int argc, const char **argv)
@@ -312,7 +317,7 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
 
        /* find existing parents */
        parent_start = buf->buf;
-       parent_start += 46; /* "tree " + "hex sha1" + "\n" */
+       parent_start += GIT_SHA1_HEXSZ + 6; /* "tree " + "hex sha1" + "\n" */
        parent_end = parent_start;
 
        while (starts_with(parent_end, "parent "))
@@ -320,11 +325,11 @@ static void replace_parents(struct strbuf *buf, int argc, const char **argv)
 
        /* prepare new parents */
        for (i = 0; i < argc; i++) {
-               unsigned char sha1[20];
-               if (get_sha1(argv[i], sha1) < 0)
+               struct object_id oid;
+               if (get_oid(argv[i], &oid) < 0)
                        die(_("Not a valid object name: '%s'"), argv[i]);
-               lookup_commit_or_die(sha1, argv[i]);
-               strbuf_addf(&new_parents, "parent %s\n", sha1_to_hex(sha1));
+               lookup_commit_or_die(oid.hash, argv[i]);
+               strbuf_addf(&new_parents, "parent %s\n", oid_to_hex(&oid));
        }
 
        /* replace existing parents with new ones */
@@ -345,12 +350,12 @@ static void check_one_mergetag(struct commit *commit,
 {
        struct check_mergetag_data *mergetag_data = (struct check_mergetag_data *)data;
        const char *ref = mergetag_data->argv[0];
-       unsigned char tag_sha1[20];
+       struct object_id tag_oid;
        struct tag *tag;
        int i;
 
-       hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_sha1);
-       tag = lookup_tag(tag_sha1);
+       hash_sha1_file(extra->value, extra->len, typename(OBJ_TAG), tag_oid.hash);
+       tag = lookup_tag(tag_oid.hash);
        if (!tag)
                die(_("bad mergetag in commit '%s'"), ref);
        if (parse_tag_buffer(tag, extra->value, extra->len))
@@ -366,7 +371,7 @@ static void check_one_mergetag(struct commit *commit,
        }
 
        die(_("original commit '%s' contains mergetag '%s' that is discarded; "
-             "use --edit instead of --graft"), ref, sha1_to_hex(tag_sha1));
+             "use --edit instead of --graft"), ref, oid_to_hex(&tag_oid));
 }
 
 static void check_mergetags(struct commit *commit, int argc, const char **argv)
@@ -380,16 +385,16 @@ static void check_mergetags(struct commit *commit, int argc, const char **argv)
 
 static int create_graft(int argc, const char **argv, int force)
 {
-       unsigned char old[20], new[20];
+       struct object_id old, new;
        const char *old_ref = argv[0];
        struct commit *commit;
        struct strbuf buf = STRBUF_INIT;
        const char *buffer;
        unsigned long size;
 
-       if (get_sha1(old_ref, old) < 0)
+       if (get_oid(old_ref, &old) < 0)
                die(_("Not a valid object name: '%s'"), old_ref);
-       commit = lookup_commit_or_die(old, old_ref);
+       commit = lookup_commit_or_die(old.hash, old_ref);
 
        buffer = get_commit_buffer(commit, &size);
        strbuf_add(&buf, buffer, size);
@@ -404,15 +409,15 @@ static int create_graft(int argc, const char **argv, int force)
 
        check_mergetags(commit, argc, argv);
 
-       if (write_sha1_file(buf.buf, buf.len, commit_type, new))
+       if (write_sha1_file(buf.buf, buf.len, commit_type, new.hash))
                die(_("could not write replacement commit for: '%s'"), old_ref);
 
        strbuf_release(&buf);
 
-       if (!hashcmp(old, new))
-               return error("new commit is the same as the old one: '%s'", sha1_to_hex(old));
+       if (!oidcmp(&old, &new))
+               return error("new commit is the same as the old one: '%s'", oid_to_hex(&old));
 
-       return replace_object_sha1(old_ref, old, "replacement", new, force);
+       return replace_object_oid(old_ref, &old, "replacement", &new, force);
 }
 
 int cmd_replace(int argc, const char **argv, const char *prefix)
index 8ab915bfcb71ae5d5f4bd1bebe7fd3c8725780b2..fc3b906c47bbcbefdc35b178016c031e46c06691 100644 (file)
@@ -256,7 +256,7 @@ static int reset_refs(const char *rev, const struct object_id *oid)
                update_ref_oid(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
                           UPDATE_REFS_MSG_ON_ERR);
        } else if (old_orig)
-               delete_ref("ORIG_HEAD", old_orig->hash, 0);
+               delete_ref(NULL, "ORIG_HEAD", old_orig->hash, 0);
        set_reflog_message(&msg, "updating HEAD", rev);
        update_ref_status = update_ref_oid(msg.buf, "HEAD", oid, orig, 0,
                                       UPDATE_REFS_MSG_ON_ERR);
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 ed8819b4516413d1597e3a3673a8eec343c82903..051333091062ef8e2e717ec4aef8a9bdf3b4450f 100644 (file)
@@ -12,6 +12,7 @@
 #include "diff.h"
 #include "revision.h"
 #include "split-index.h"
+#include "submodule.h"
 
 #define DO_REVS                1
 #define DO_NOREV       2
@@ -204,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)
@@ -227,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;
@@ -573,6 +575,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
        unsigned int flags = 0;
        const char *name = NULL;
        struct object_context unused;
+       struct strbuf buf = STRBUF_INIT;
 
        if (argc > 1 && !strcmp("--parseopt", argv[1]))
                return cmd_parseopt(argc - 1, argv + 1, prefix);
@@ -627,7 +630,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                if (!strcmp(arg, "--git-path")) {
                        if (!argv[i + 1])
                                die("--git-path requires an argument");
-                       puts(git_path("%s", argv[i + 1]));
+                       strbuf_reset(&buf);
+                       puts(relative_path(git_path("%s", argv[i + 1]),
+                                          prefix, &buf));
                        i++;
                        continue;
                }
@@ -781,6 +786,12 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                        puts(work_tree);
                                continue;
                        }
+                       if (!strcmp(arg, "--show-superproject-working-tree")) {
+                               const char *superproject = get_superproject_working_tree();
+                               if (superproject)
+                                       puts(superproject);
+                               continue;
+                       }
                        if (!strcmp(arg, "--show-prefix")) {
                                if (prefix)
                                        puts(prefix);
@@ -807,17 +818,27 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                putchar('\n');
                                continue;
                        }
-                       if (!strcmp(arg, "--git-dir")) {
+                       if (!strcmp(arg, "--git-dir") ||
+                           !strcmp(arg, "--absolute-git-dir")) {
                                const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
                                char *cwd;
                                int len;
-                               if (gitdir) {
-                                       puts(gitdir);
-                                       continue;
-                               }
-                               if (!prefix) {
-                                       puts(".git");
-                                       continue;
+                               if (arg[2] == 'g') {    /* --git-dir */
+                                       if (gitdir) {
+                                               puts(gitdir);
+                                               continue;
+                                       }
+                                       if (!prefix) {
+                                               puts(".git");
+                                               continue;
+                                       }
+                               } else {                /* --absolute-git-dir */
+                                       if (!gitdir && !prefix)
+                                               gitdir = ".git";
+                                       if (gitdir) {
+                                               puts(real_path(gitdir));
+                                               continue;
+                                       }
                                }
                                cwd = xgetcwd();
                                len = strlen(cwd);
@@ -826,8 +847,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                continue;
                        }
                        if (!strcmp(arg, "--git-common-dir")) {
-                               const char *pfx = prefix ? prefix : "";
-                               puts(prefix_filename(pfx, strlen(pfx), get_git_common_dir()));
+                               strbuf_reset(&buf);
+                               puts(relative_path(get_git_common_dir(),
+                                                  prefix, &buf));
                                continue;
                        }
                        if (!strcmp(arg, "--is-inside-git-dir")) {
@@ -850,7 +872,9 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                        die(_("Could not read the index"));
                                if (the_index.split_index) {
                                        const unsigned char *sha1 = the_index.split_index->base_sha1;
-                                       puts(git_path("sharedindex.%s", sha1_to_hex(sha1)));
+                                       const char *path = git_path("sharedindex.%s", sha1_to_hex(sha1));
+                                       strbuf_reset(&buf);
+                                       puts(relative_path(path, prefix, &buf));
                                }
                                continue;
                        }
@@ -902,6 +926,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                        continue;
                verify_filename(prefix, arg, 1);
        }
+       strbuf_release(&buf);
        if (verify) {
                if (revs_count == 1) {
                        show_rev(type, sha1, name);
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 677f372083c0f726b0d773ca502a4d6e09cb98eb..7cff1839fc15f46d58c9f78361f362d1d790cbd9 100644 (file)
@@ -148,7 +148,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
 
        ctx.fmt = CMIT_FMT_USERFORMAT;
        ctx.abbrev = log->abbrev;
-       ctx.subject = "";
+       ctx.print_email_subject = 1;
        ctx.date_mode.type = DATE_NORMAL;
        ctx.output_encoding = get_log_output_encoding();
 
index 899dc334e323a53f3e8a2981a1f8140f55f136a9..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
@@ -356,12 +391,10 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
                        strbuf_addf(&remotesb, "remote.%s.url", remote);
                        free(remote);
 
-                       if (git_config_get_string(remotesb.buf, &remoteurl))
-                               /*
-                                * The repository is its own
-                                * authoritative upstream
-                                */
+                       if (git_config_get_string(remotesb.buf, &remoteurl)) {
+                               warning(_("could not lookup configuration '%s'. Assuming this repository is its own authoritative upstream."), remotesb.buf);
                                remoteurl = xgetcwd();
+                       }
                        relurl = relative_url(remoteurl, url, NULL);
                        strbuf_release(&remotesb);
                        free(remoteurl);
@@ -422,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);
 
@@ -579,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;
@@ -653,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);
@@ -689,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);
@@ -761,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)) {
@@ -795,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;
        }
@@ -837,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)
@@ -847,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);
 
@@ -1092,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;
@@ -1129,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 {
@@ -1147,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 96eed944683a45c33fa5d4a05414ae56406fafca..70addef158b347488366f6c5787d326c3657adcd 100644 (file)
@@ -58,7 +58,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)
                        die("Cannot delete %s, not a symbolic ref", argv[0]);
                if (!strcmp(argv[0], "HEAD"))
                        die("deleting '%s' is not allowed", argv[0]);
-               return delete_ref(argv[0], NULL, REF_NODEREF);
+               return delete_ref(NULL, argv[0], NULL, REF_NODEREF);
        }
 
        switch (argc) {
index e40c4a96763a371b104a5bf47e6d839ed93e8ef5..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
@@ -45,11 +45,11 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
        if (!format) {
                if (filter->lines) {
                        to_free = xstrfmt("%s %%(contents:lines=%d)",
-                                         "%(align:15)%(refname:strip=2)%(end)",
+                                         "%(align:15)%(refname:lstrip=2)%(end)",
                                          filter->lines);
                        format = to_free;
                } else
-                       format = "%(refname:strip=2)";
+                       format = "%(refname:lstrip=2)";
        }
 
        verify_ref_format(format);
@@ -72,32 +72,29 @@ 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;
 }
 
 static int delete_tag(const char *name, const char *ref,
                      const unsigned char *sha1, const void *cb_data)
 {
-       if (delete_ref(ref, sha1, 0))
+       if (delete_ref(NULL, ref, sha1, 0))
                return 1;
        printf(_("Deleted tag '%s' (was %s)\n"), name, find_unique_abbrev(sha1, DEFAULT_ABBREV));
        return 0;
@@ -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)
@@ -302,6 +296,54 @@ static void create_tag(const unsigned char *object, const char *tag,
        }
 }
 
+static void create_reflog_msg(const unsigned char *sha1, struct strbuf *sb)
+{
+       enum object_type type;
+       struct commit *c;
+       char *buf;
+       unsigned long size;
+       int subject_len = 0;
+       const char *subject_start;
+
+       char *rla = getenv("GIT_REFLOG_ACTION");
+       if (rla) {
+               strbuf_addstr(sb, rla);
+       } else {
+               strbuf_addstr(sb, _("tag: tagging "));
+               strbuf_add_unique_abbrev(sb, sha1, DEFAULT_ABBREV);
+       }
+
+       strbuf_addstr(sb, " (");
+       type = sha1_object_info(sha1, NULL);
+       switch (type) {
+       default:
+               strbuf_addstr(sb, _("object of unknown type"));
+               break;
+       case OBJ_COMMIT:
+               if ((buf = read_sha1_file(sha1, &type, &size)) != NULL) {
+                       subject_len = find_commit_subject(buf, &subject_start);
+                       strbuf_insert(sb, sb->len, subject_start, subject_len);
+               } else {
+                       strbuf_addstr(sb, _("commit object"));
+               }
+               free(buf);
+
+               if ((c = lookup_commit_reference(sha1)) != NULL)
+                       strbuf_addf(sb, ", %s", show_date(c->date, 0, DATE_MODE(SHORT)));
+               break;
+       case OBJ_TREE:
+               strbuf_addstr(sb, _("tree object"));
+               break;
+       case OBJ_BLOB:
+               strbuf_addstr(sb, _("blob object"));
+               break;
+       case OBJ_TAG:
+               strbuf_addstr(sb, _("other tag object"));
+               break;
+       }
+       strbuf_addch(sb, ')');
+}
+
 struct msg_arg {
        int given;
        struct strbuf buf;
@@ -335,6 +377,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 {
        struct strbuf buf = STRBUF_INIT;
        struct strbuf ref = STRBUF_INIT;
+       struct strbuf reflog_msg = STRBUF_INIT;
        unsigned char object[20], prev[20];
        const char *object_ref, *tag;
        struct create_tag_options opt;
@@ -375,20 +418,25 @@ 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")),
                OPT_END()
        };
 
+       setup_ref_filter_porcelain_msg();
+
        git_config(git_tag_config, sorting_tail);
 
        memset(&opt, 0, sizeof(opt));
@@ -403,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);
@@ -434,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') {
@@ -494,6 +550,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        else
                die(_("Invalid cleanup mode %s"), cleanup_arg);
 
+       create_reflog_msg(object, &reflog_msg);
+
        if (create_tag_object) {
                if (force_sign_annotate && !annotate)
                        opt.sign = 1;
@@ -504,7 +562,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        if (!transaction ||
            ref_transaction_update(transaction, ref.buf, object, prev,
                                   create_reflog ? REF_FORCE_CREATE_REFLOG : 0,
-                                  NULL, &err) ||
+                                  reflog_msg.buf, &err) ||
            ref_transaction_commit(transaction, &err))
                die("%s", err.buf);
        ref_transaction_free(transaction);
@@ -514,5 +572,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
        strbuf_release(&err);
        strbuf_release(&buf);
        strbuf_release(&ref);
+       strbuf_release(&reflog_msg);
        return 0;
 }
index d530e89368b42bf1465784b80163b9158a06585a..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);
@@ -1099,17 +1103,20 @@ int cmd_update_index(int argc, const char **argv, const char *prefix)
        }
 
        if (split_index > 0) {
-               init_split_index(&the_index);
-               the_index.cache_changed |= SPLIT_INDEX_ORDERED;
-       } else if (!split_index && the_index.split_index) {
-               /*
-                * can't discard_split_index(&the_index); because that
-                * will destroy split_index->base->cache[], which may
-                * be shared with the_index.cache[]. So yeah we're
-                * leaking a bit here.
-                */
-               the_index.split_index = NULL;
-               the_index.cache_changed |= SOMETHING_CHANGED;
+               if (git_config_get_split_index() == 0)
+                       warning(_("core.splitIndex is set to false; "
+                                 "remove or change it, if you really want to "
+                                 "enable split index"));
+               if (the_index.split_index)
+                       the_index.cache_changed |= SPLIT_INDEX_ORDERED;
+               else
+                       add_split_index(&the_index);
+       } else if (!split_index) {
+               if (git_config_get_split_index() == 1)
+                       warning(_("core.splitIndex is set to true; "
+                                 "remove or change it, if you really want to "
+                                 "disable split index"));
+               remove_split_index(&the_index);
        }
 
        switch (untracked_cache) {
index 7f30d3a76f69e58ae3cdbbe05c5ebdce6c8a120d..0b2ecf41aeda427e75eb7fe36388e7d0d91d5413 100644 (file)
@@ -433,7 +433,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
                 * For purposes of backwards compatibility, we treat
                 * NULL_SHA1 as "don't care" here:
                 */
-               return delete_ref(refname,
+               return delete_ref(msg, refname,
                                  (oldval && !is_null_sha1(oldsha1)) ? oldsha1 : NULL,
                                  flags);
        else
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 900796c155be49d8c275c735edf1c7a2dabec70a..322ae582590e40a9691ff390ba6e4d1589c8b550 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)
@@ -1069,8 +1096,9 @@ int adjust_shared_perm(const char *path);
 
 /*
  * Create the directory containing the named path, using care to be
- * somewhat safe against races.  Return one of the scld_error values
- * to indicate success/failure.
+ * somewhat safe against races. Return one of the scld_error values to
+ * indicate success/failure. On error, set errno to describe the
+ * problem.
  *
  * SCLD_VANISHED indicates that one of the ancestor directories of the
  * path existed at one point during the function call and then
@@ -1094,8 +1122,51 @@ enum scld_error {
 enum scld_error safe_create_leading_directories(char *path);
 enum scld_error safe_create_leading_directories_const(const char *path);
 
+/*
+ * Callback function for raceproof_create_file(). This function is
+ * expected to do something that makes dirname(path) permanent despite
+ * the fact that other processes might be cleaning up empty
+ * directories at the same time. Usually it will create a file named
+ * path, but alternatively it could create another file in that
+ * directory, or even chdir() into that directory. The function should
+ * return 0 if the action was completed successfully. On error, it
+ * should return a nonzero result and set errno.
+ * raceproof_create_file() treats two errno values specially:
+ *
+ * - ENOENT -- dirname(path) does not exist. In this case,
+ *             raceproof_create_file() tries creating dirname(path)
+ *             (and any parent directories, if necessary) and calls
+ *             the function again.
+ *
+ * - EISDIR -- the file already exists and is a directory. In this
+ *             case, raceproof_create_file() removes the directory if
+ *             it is empty (and recursively any empty directories that
+ *             it contains) and calls the function again.
+ *
+ * Any other errno causes raceproof_create_file() to fail with the
+ * callback's return value and errno.
+ *
+ * Obviously, this function should be OK with being called again if it
+ * fails with ENOENT or EISDIR. In other scenarios it will not be
+ * called again.
+ */
+typedef int create_file_fn(const char *path, void *cb);
+
+/*
+ * Create a file in dirname(path) by calling fn, creating leading
+ * directories if necessary. Retry a few times in case we are racing
+ * with another process that is trying to clean up the directory that
+ * contains path. See the documentation for create_file_fn for more
+ * details.
+ *
+ * Return the value and set the errno that resulted from the most
+ * recent call of fn. fn is always called at least once, and will be
+ * called more than once if it returns ENOENT or EISDIR.
+ */
+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)
 {
@@ -1125,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
@@ -1226,6 +1304,9 @@ extern int has_pack_index(const unsigned char *sha1);
 
 extern void assert_sha1_type(const unsigned char *sha1, enum object_type expect);
 
+/* Helper to check and "touch" a file */
+extern int check_and_freshen_file(const char *fn, int freshen);
+
 extern const signed char hexval_table[256];
 static inline unsigned int hexval(unsigned char c)
 {
@@ -1285,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);
@@ -1316,6 +1397,15 @@ extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
 extern char *oid_to_hex(const struct object_id *oid);  /* same static buffer as sha1_to_hex */
 
+/*
+ * Parse a 40-character hexadecimal object ID starting from hex, updating the
+ * pointer specified by end when parsing stops.  The resulting object ID is
+ * stored in oid.  Returns 0 on success.  Parsing will stop on the first NUL or
+ * other invalid character.  end is only updated on success; otherwise, it is
+ * unmodified.
+ */
+extern int parse_oid_hex(const char *hex, struct object_id *oid, const char **end);
+
 /*
  * This reads short-hand syntax that not only evaluates to a commit
  * object name, but also can act as if the end user spelled the name
@@ -1591,9 +1681,12 @@ 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.
+ * 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(char *template, size_t limit, const char *pattern);
+extern int odb_mkstemp(struct strbuf *template, const char *pattern);
 
 /*
  * Generate the filename to be used for a pack file with checksum "sha1" and
@@ -1647,6 +1740,12 @@ extern void check_pack_index_ptr(const struct packed_git *p, const void *ptr);
  * error.
  */
 extern const unsigned char *nth_packed_object_sha1(struct packed_git *, uint32_t n);
+/*
+ * Like nth_packed_object_sha1, but write the data into the object specified by
+ * the the first argument.  Returns the first argument on success, and NULL on
+ * error.
+ */
+extern const struct object_id *nth_packed_object_oid(struct object_id *, struct packed_git *, uint32_t n);
 
 /*
  * Return the offset of the nth object within the specified packfile.
@@ -1688,7 +1787,7 @@ extern int unpack_object_header(struct packed_git *, struct pack_window **, off_
  * scratch buffer, but restored to its original contents before
  * the function returns.
  */
-typedef int each_loose_object_fn(const unsigned char *sha1,
+typedef int each_loose_object_fn(const struct object_id *oid,
                                 const char *path,
                                 void *data);
 typedef int each_loose_cruft_fn(const char *basename,
@@ -1714,7 +1813,7 @@ int for_each_loose_file_in_objdir_buf(struct strbuf *path,
  * LOCAL_ONLY flag is set).
  */
 #define FOR_EACH_OBJECT_LOCAL_ONLY 0x1
-typedef int each_packed_object_fn(const unsigned char *sha1,
+typedef int each_packed_object_fn(const struct object_id *oid,
                                  struct packed_git *pack,
                                  uint32_t pos,
                                  void *data);
@@ -1792,6 +1891,11 @@ enum config_origin_type {
        CONFIG_ORIGIN_CMDLINE
 };
 
+struct config_options {
+       unsigned int respect_includes : 1;
+       const char *git_dir;
+};
+
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
@@ -1801,15 +1905,17 @@ 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,
-                                  int respect_includes);
+                                  const struct config_options *opts);
 extern int git_parse_ulong(const char *, unsigned long *);
 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 *);
@@ -1856,6 +1962,7 @@ struct config_include_data {
        int depth;
        config_fn_t fn;
        void *data;
+       const struct config_options *opts;
 };
 #define CONFIG_INCLUDE_INIT { 0 }
 extern int git_config_include(const char *name, const char *value, void *data);
@@ -1933,6 +2040,11 @@ extern int git_config_get_bool_or_int(const char *key, int *is_bool, int *dest);
 extern int git_config_get_maybe_bool(const char *key, int *dest);
 extern int git_config_get_pathname(const char *key, const char **dest);
 extern int git_config_get_untracked_cache(void);
+extern int git_config_get_split_index(void);
+extern int git_config_get_max_percent_split_change(void);
+
+/* This dies if the configured or default date is in the future */
+extern int git_config_get_expiry(const char *key, const char **output);
 
 /*
  * This is a hack for test programs like test-dump-untracked-cache to
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 fab8269731b4764351c7b68543337ded138d4aab..73c78c2b80c1a21e83f1942347a76fb76511a0d3 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -415,8 +415,7 @@ int find_commit_subject(const char *commit_buffer, const char **subject)
                p++;
        if (*p) {
                p = skip_blank_lines(p + 2);
-               for (eol = p; *eol && *eol != '\n'; eol++)
-                       ; /* do nothing */
+               eol = strchrnul(p, '\n');
        } else
                eol = p;
 
index 9c12abb91110151e3a982c4d8baf8753d0382441..7b1986d5c8a0120ea90ee485ba46aff912de60fa 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -142,21 +142,24 @@ static inline int cmit_fmt_is_mail(enum cmit_fmt fmt)
        return (fmt == CMIT_FMT_EMAIL || fmt == CMIT_FMT_MBOXRD);
 }
 
+struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
+
 struct pretty_print_context {
        /*
         * Callers should tweak these to change the behavior of pp_* functions.
         */
        enum cmit_fmt fmt;
        int abbrev;
-       const char *subject;
        const char *after_subject;
        int preserve_subject;
        struct date_mode date_mode;
        unsigned date_mode_explicit:1;
+       int print_email_subject;
        int expand_tabs_in_log;
        int need_8bit_cte;
        char *notes_message;
        struct reflog_walk_info *reflog_info;
+       struct rev_info *rev;
        const char *output_encoding;
        struct string_list *mailmap;
        int color;
@@ -175,7 +178,6 @@ struct userformat_want {
 };
 
 extern int has_non_ascii(const char *text);
-struct rev_info; /* in revision.h, it circularly uses enum cmit_fmt */
 extern const char *logmsg_reencode(const struct commit *commit,
                                   char **commit_encoding,
                                   const char *output_encoding);
@@ -259,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);
@@ -271,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;
@@ -293,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 c654f95551c33d6b346ae7fe43a6cac987dd6b75..6a689007e7ce3fe08f148e8b82c0a1c618c513a5 100644 (file)
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "exec_cmd.h"
+#include "attr.h"
 
 /*
  * Many parts of Git have subprograms communicate via pipe, expect the
@@ -33,6 +34,8 @@ int main(int argc, const char **argv)
 
        git_setup_gettext();
 
+       attr_start();
+
        git_extract_argv0_path(argv[0]);
 
        restore_sigpipe_to_default();
index 0e9e1ebefc0a6771de7bf20615e41176ed986179..b4a3205da32faf43db1ab990f08c0bb941af87d0 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,104 @@ 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 struct config_options *opts,
+                            const char *cond, size_t cond_len, int icase)
+{
+       struct strbuf text = STRBUF_INIT;
+       struct strbuf pattern = STRBUF_INIT;
+       int ret = 0, prefix;
+       const char *git_dir;
+
+       if (opts->git_dir)
+               git_dir = opts->git_dir;
+       else if (have_git_dir())
+               git_dir = get_git_dir();
+       else
+               goto done;
+
+       strbuf_realpath(&text, 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 struct config_options *opts,
+                                    const char *cond, size_t cond_len)
+{
+
+       if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
+               return include_by_gitdir(opts, cond, cond_len, 0);
+       else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
+               return include_by_gitdir(opts, 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 +281,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(inc->opts, cond, cond_len)) &&
+           !strcmp(key, "path"))
+               ret = handle_path_include(value, inc);
+
        return ret;
 }
 
@@ -742,6 +844,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 +911,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 +975,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;
@@ -1402,12 +1521,20 @@ int git_config_system(void)
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-static int do_git_config_sequence(config_fn_t fn, void *data)
+static int do_git_config_sequence(const struct config_options *opts,
+                                 config_fn_t fn, void *data)
 {
        int ret = 0;
        char *xdg_config = xdg_config_home("config");
-       char *user_config = expand_user_path("~/.gitconfig");
-       char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;
+       char *user_config = expand_user_path("~/.gitconfig", 0);
+       char *repo_config;
+
+       if (opts->git_dir)
+               repo_config = mkpathdup("%s/config", opts->git_dir);
+       else if (have_git_dir())
+               repo_config = git_pathdup("config");
+       else
+               repo_config = NULL;
 
        current_parsing_scope = CONFIG_SCOPE_SYSTEM;
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
@@ -1438,13 +1565,14 @@ static int do_git_config_sequence(config_fn_t fn, void *data)
 
 int git_config_with_options(config_fn_t fn, void *data,
                            struct git_config_source *config_source,
-                           int respect_includes)
+                           const struct config_options *opts)
 {
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
 
-       if (respect_includes) {
+       if (opts->respect_includes) {
                inc.fn = fn;
                inc.data = data;
+               inc.opts = opts;
                fn = git_config_include;
                data = &inc;
        }
@@ -1460,12 +1588,15 @@ int git_config_with_options(config_fn_t fn, void *data,
        else if (config_source && config_source->blob)
                return git_config_from_blob_ref(fn, config_source->blob, data);
 
-       return do_git_config_sequence(fn, data);
+       return do_git_config_sequence(opts, fn, data);
 }
 
 static void git_config_raw(config_fn_t fn, void *data)
 {
-       if (git_config_with_options(fn, data, NULL, 1) < 0)
+       struct config_options opts = {0};
+
+       opts.respect_includes = 1;
+       if (git_config_with_options(fn, data, NULL, &opts) < 0)
                /*
                 * git_config_with_options() normally returns only
                 * zero, as most errors are fatal, and
@@ -1503,6 +1634,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 config_options opts = {0};
+       struct strbuf buf = STRBUF_INIT;
+
+       opts.respect_includes = 1;
+
+       if (have_git_dir())
+               opts.git_dir = get_git_dir();
+       /*
+        * 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).
+        */
+       else if (discover_git_directory(&buf))
+               opts.git_dir = buf.buf;
+
+       git_config_with_options(cb, data, NULL, &opts);
+
+       strbuf_release(&buf);
+}
+
 static void git_config_check_init(void);
 
 void git_config(config_fn_t fn, void *data)
@@ -1803,6 +1959,19 @@ int git_config_get_pathname(const char *key, const char **dest)
        return ret;
 }
 
+int git_config_get_expiry(const char *key, const char **output)
+{
+       int ret = git_config_get_string_const(key, output);
+       if (ret)
+               return ret;
+       if (strcmp(*output, "now")) {
+               unsigned long now = approxidate("now");
+               if (approxidate(*output) >= now)
+                       git_die_config(key, _("Invalid %s: '%s'"), key, *output);
+       }
+       return ret;
+}
+
 int git_config_get_untracked_cache(void)
 {
        int val = -1;
@@ -1819,14 +1988,39 @@ int git_config_get_untracked_cache(void)
                if (!strcasecmp(v, "keep"))
                        return -1;
 
-               error("unknown core.untrackedCache value '%s'; "
-                     "using 'keep' default value", v);
+               error(_("unknown core.untrackedCache value '%s'; "
+                       "using 'keep' default value"), v);
                return -1;
        }
 
        return -1; /* default value */
 }
 
+int git_config_get_split_index(void)
+{
+       int val;
+
+       if (!git_config_get_maybe_bool("core.splitindex", &val))
+               return val;
+
+       return -1; /* default value */
+}
+
+int git_config_get_max_percent_split_change(void)
+{
+       int val = -1;
+
+       if (!git_config_get_int("splitindex.maxpercentchange", &val)) {
+               if (0 <= val && val <= 100)
+                       return val;
+
+               return error(_("splitIndex.maxPercentChange value '%d' "
+                              "should be between 0 and 100"), val);
+       }
+
+       return -1; /* default value */
+}
+
 NORETURN
 void git_die_config_linenr(const char *key, const char *filename, int linenr)
 {
index 8cb93b0720d9a33d8fc0cddb21c3d1c1a789da96..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;
                }
 
@@ -691,6 +691,68 @@ static const char *get_ssh_command(void)
        return NULL;
 }
 
+static int override_ssh_variant(int *port_option, int *needs_batch)
+{
+       char *variant;
+
+       variant = xstrdup_or_null(getenv("GIT_SSH_VARIANT"));
+       if (!variant &&
+           git_config_get_string("ssh.variant", &variant))
+               return 0;
+
+       if (!strcmp(variant, "plink") || !strcmp(variant, "putty")) {
+               *port_option = 'P';
+               *needs_batch = 0;
+       } else if (!strcmp(variant, "tortoiseplink")) {
+               *port_option = 'P';
+               *needs_batch = 1;
+       } else {
+               *port_option = 'p';
+               *needs_batch = 0;
+       }
+       free(variant);
+       return 1;
+}
+
+static void handle_ssh_variant(const char *ssh_command, int is_cmdline,
+                              int *port_option, int *needs_batch)
+{
+       const char *variant;
+       char *p = NULL;
+
+       if (override_ssh_variant(port_option, needs_batch))
+               return;
+
+       if (!is_cmdline) {
+               p = xstrdup(ssh_command);
+               variant = basename(p);
+       } else {
+               const char **ssh_argv;
+
+               p = xstrdup(ssh_command);
+               if (split_cmdline(p, &ssh_argv) > 0) {
+                       variant = basename((char *)ssh_argv[0]);
+                       /*
+                        * At this point, variant points into the buffer
+                        * referenced by p, hence we do not need ssh_argv
+                        * any longer.
+                        */
+                       free(ssh_argv);
+               } else
+                       return;
+       }
+
+       if (!strcasecmp(variant, "plink") ||
+           !strcasecmp(variant, "plink.exe"))
+               *port_option = 'P';
+       else if (!strcasecmp(variant, "tortoiseplink") ||
+                !strcasecmp(variant, "tortoiseplink.exe")) {
+               *port_option = 'P';
+               *needs_batch = 1;
+       }
+       free(p);
+}
+
 /*
  * This returns a dummy child_process if the transport protocol does not
  * need fork(2), or a struct child_process object if it does.  Once done,
@@ -769,7 +831,8 @@ struct child_process *git_connect(int fd[2], const char *url,
                conn->in = conn->out = -1;
                if (protocol == PROTO_SSH) {
                        const char *ssh;
-                       int putty = 0, tortoiseplink = 0;
+                       int needs_batch = 0;
+                       int port_option = 'p';
                        char *ssh_host = hostandport;
                        const char *port = NULL;
                        transport_check_allowed("ssh");
@@ -792,10 +855,10 @@ struct child_process *git_connect(int fd[2], const char *url,
                        }
 
                        ssh = get_ssh_command();
-                       if (!ssh) {
-                               const char *base;
-                               char *ssh_dup;
-
+                       if (ssh)
+                               handle_ssh_variant(ssh, 1, &port_option,
+                                                  &needs_batch);
+                       else {
                                /*
                                 * GIT_SSH is the no-shell version of
                                 * GIT_SSH_COMMAND (and must remain so for
@@ -806,17 +869,10 @@ struct child_process *git_connect(int fd[2], const char *url,
                                ssh = getenv("GIT_SSH");
                                if (!ssh)
                                        ssh = "ssh";
-
-                               ssh_dup = xstrdup(ssh);
-                               base = basename(ssh_dup);
-
-                               tortoiseplink = !strcasecmp(base, "tortoiseplink") ||
-                                       !strcasecmp(base, "tortoiseplink.exe");
-                               putty = tortoiseplink ||
-                                       !strcasecmp(base, "plink") ||
-                                       !strcasecmp(base, "plink.exe");
-
-                               free(ssh_dup);
+                               else
+                                       handle_ssh_variant(ssh, 0,
+                                                          &port_option,
+                                                          &needs_batch);
                        }
 
                        argv_array_push(&conn->args, ssh);
@@ -824,11 +880,11 @@ struct child_process *git_connect(int fd[2], const char *url,
                                argv_array_push(&conn->args, "-4");
                        else if (flags & CONNECT_IPV6)
                                argv_array_push(&conn->args, "-6");
-                       if (tortoiseplink)
+                       if (needs_batch)
                                argv_array_push(&conn->args, "-batch");
                        if (port) {
-                               /* P is for PuTTY, p is for OpenSSH */
-                               argv_array_push(&conn->args, putty ? "-P" : "-p");
+                               argv_array_pushf(&conn->args,
+                                                "-%c", port_option);
                                argv_array_push(&conn->args, port);
                        }
                        argv_array_push(&conn->args, ssh_host);
index 41ee52991d0c1b1fa2a6021133cffecd3ca9deb0..1150164d5ce0e7396191a192189b43444883f739 100644 (file)
@@ -34,21 +34,41 @@ case "$COMP_WORDBREAKS" in
 *)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
 esac
 
+# Discovers the path to the git repository taking any '--git-dir=<path>' and
+# '-C <path>' options into account and stores it in the $__git_repo_path
+# variable.
+__git_find_repo_path ()
+{
+       if [ -n "$__git_repo_path" ]; then
+               # we already know where it is
+               return
+       fi
+
+       if [ -n "${__git_C_args-}" ]; then
+               __git_repo_path="$(git "${__git_C_args[@]}" \
+                       ${__git_dir:+--git-dir="$__git_dir"} \
+                       rev-parse --absolute-git-dir 2>/dev/null)"
+       elif [ -n "${__git_dir-}" ]; then
+               test -d "$__git_dir" &&
+               __git_repo_path="$__git_dir"
+       elif [ -n "${GIT_DIR-}" ]; then
+               test -d "${GIT_DIR-}" &&
+               __git_repo_path="$GIT_DIR"
+       elif [ -d .git ]; then
+               __git_repo_path=.git
+       else
+               __git_repo_path="$(git rev-parse --git-dir 2>/dev/null)"
+       fi
+}
+
+# Deprecated: use __git_find_repo_path() and $__git_repo_path instead
 # __gitdir accepts 0 or 1 arguments (i.e., location)
 # returns location of .git repo
 __gitdir ()
 {
        if [ -z "${1-}" ]; then
-               if [ -n "${__git_dir-}" ]; then
-                       echo "$__git_dir"
-               elif [ -n "${GIT_DIR-}" ]; then
-                       test -d "${GIT_DIR-}" || return 1
-                       echo "$GIT_DIR"
-               elif [ -d .git ]; then
-                       echo .git
-               else
-                       git rev-parse --git-dir 2>/dev/null
-               fi
+               __git_find_repo_path || return 1
+               echo "$__git_repo_path"
        elif [ -d "$1/.git" ]; then
                echo "$1/.git"
        else
@@ -56,6 +76,14 @@ __gitdir ()
        fi
 }
 
+# Runs git with all the options given as argument, respecting any
+# '--git-dir=<path>' and '-C <path>' options present on the command line
+__git ()
+{
+       git ${__git_C_args:+"${__git_C_args[@]}"} \
+               ${__git_dir:+--git-dir="$__git_dir"} "$@" 2>/dev/null
+}
+
 # The following function is based on code from:
 #
 #   bash_completion - programmable completion functions for bash 3.2+
@@ -185,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[@]}
@@ -283,11 +325,11 @@ __gitcomp_file ()
 __git_ls_files_helper ()
 {
        if [ "$2" == "--committable" ]; then
-               git -C "$1" diff-index --name-only --relative HEAD
+               __git -C "$1" diff-index --name-only --relative HEAD
        else
                # NOTE: $2 is not quoted in order to support multiple options
-               git -C "$1" ls-files --exclude-standard $2
-       fi 2>/dev/null
+               __git -C "$1" ls-files --exclude-standard $2
+       fi
 }
 
 
@@ -299,100 +341,195 @@ __git_ls_files_helper ()
 #    slash.
 __git_index_files ()
 {
-       local dir="$(__gitdir)" root="${2-.}" file
+       local root="${2-.}" file
 
-       if [ -d "$dir" ]; then
-               __git_ls_files_helper "$root" "$1" |
-               while read -r file; do
-                       case "$file" in
-                       ?*/*) echo "${file%%/*}" ;;
-                       *) echo "$file" ;;
-                       esac
-               done | sort | uniq
-       fi
+       __git_ls_files_helper "$root" "$1" |
+       while read -r file; do
+               case "$file" in
+               ?*/*) echo "${file%%/*}" ;;
+               *) echo "$file" ;;
+               esac
+       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 ()
 {
-       local dir="$(__gitdir)"
-       if [ -d "$dir" ]; then
-               git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
-                       refs/heads
-               return
-       fi
+       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 ()
 {
-       local dir="$(__gitdir)"
-       if [ -d "$dir" ]; then
-               git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
-                       refs/tags
-               return
-       fi
+       local pfx="${1-}" cur_="${2-}" sfx="${3-}"
+
+       __git for-each-ref --format="${pfx//\%/%%}%(refname:strip=2)$sfx" \
+                       "refs/tags/$cur_*" "refs/tags/$cur_*/**"
 }
 
-# __git_refs accepts 0, 1 (to pass to __gitdir), or 2 arguments
-# presence of 2nd argument means use the guess heuristic employed
-# by checkout for tracking branches
+# Lists refs from the local (by default) or from a remote repository.
+# It accepts 0, 1 or 2 arguments:
+# 1: The remote to list refs from (optional; ignored, if set but empty).
+#    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="$(__gitdir "${1-}")" track="${2-}"
-       local format refs pfx
-       if [ -d "$dir" ]; then
-               case "$cur" in
+       local i hash dir track="${2-}"
+       local list_refs_from=path remote="${1-}"
+       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"
+
+       if [ -z "$remote" ]; then
+               if [ -z "$dir" ]; then
+                       return
+               fi
+       else
+               if __git_is_configured_remote "$remote"; then
+                       # configured remote takes precedence over a
+                       # local directory with the same name
+                       list_refs_from=remote
+               elif [ -d "$remote/.git" ]; then
+                       dir="$remote/.git"
+               elif [ -d "$remote" ]; then
+                       dir="$remote"
+               else
+                       list_refs_from=url
+               fi
+       fi
+
+       if [ "$list_refs_from" = path ]; then
+               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 --git-dir="$dir" 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 --git-dir="$dir" 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 "$dir" "$cur*" 2>/dev/null | \
+               __git ls-remote "$remote" "$match*" | \
                while read -r hash i; do
                        case "$i" in
                        *^{}) ;;
-                       *) echo "$i" ;;
+                       *) echo "$pfx$i$sfx" ;;
                        esac
                done
                ;;
        *)
-               echo "HEAD"
-               git for-each-ref --format="%(refname:short)" -- \
-                       "refs/remotes/$dir/" 2>/dev/null | sed -e "s#^$dir/##"
+               if [ "$list_refs_from" = remote ]; then
+                       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
+                       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 "$pfx${i#refs/*/}$sfx" ;;
+                               *)      echo "$pfx$i$sfx" ;;  # symbolic refs
+                               esac
+                       done
+               fi
                ;;
        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
@@ -401,11 +538,29 @@ __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 ()
 {
        local i hash
-       git ls-remote "$1" 'refs/heads/*' 2>/dev/null | \
+       __git ls-remote "$1" 'refs/heads/*' | \
        while read -r hash i; do
                echo "$i:refs/remotes/$1/${i#refs/heads/}"
        done
@@ -413,9 +568,21 @@ __git_refs_remotes ()
 
 __git_remotes ()
 {
-       local d="$(__gitdir)"
-       test -d "$d/remotes" && ls -1 "$d/remotes"
-       git --git-dir="$d" remote
+       __git_find_repo_path
+       test -d "$__git_repo_path/remotes" && ls -1 "$__git_repo_path/remotes"
+       __git remote
+}
+
+# Returns true if $1 matches the name of a configured remote, false otherwise.
+__git_is_configured_remote ()
+{
+       local remote
+       for remote in $(__git_remotes); do
+               if [ "$remote" = "$1" ]; then
+                       return 0
+               fi
+       done
+       return 1
 }
 
 __git_list_merge_strategies ()
@@ -469,7 +636,7 @@ __git_complete_revlist_file ()
                *)   pfx="$ref:$pfx" ;;
                esac
 
-               __gitcomp_nl "$(git --git-dir="$(__gitdir)" ls-tree "$ls" 2>/dev/null \
+               __gitcomp_nl "$(__git ls-tree "$ls" \
                                | sed '/^100... blob /{
                                           s,^.*        ,,
                                           s,$, ,
@@ -488,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
 }
@@ -581,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
@@ -747,7 +914,7 @@ __git_compute_porcelain_commands ()
 __git_get_config_variables ()
 {
        local section="$1" i IFS=$'\n'
-       for i in $(git --git-dir="$(__gitdir)" config --name-only --get-regexp "^$section\..*" 2>/dev/null); do
+       for i in $(__git config --name-only --get-regexp "^$section\..*"); do
                echo "${i#$section.}"
        done
 }
@@ -765,8 +932,7 @@ __git_aliases ()
 # __git_aliased_command requires 1 argument
 __git_aliased_command ()
 {
-       local word cmdline=$(git --git-dir="$(__gitdir)" \
-               config --get "alias.$1")
+       local word cmdline=$(__git config --get "alias.$1")
        for word in $cmdline; do
                case "$word" in
                \!gitk|gitk)
@@ -842,7 +1008,7 @@ __git_get_option_value ()
        done
 
        if [ -n "$config_key" ] && [ -z "$result" ]; then
-               result="$(git --git-dir="$(__gitdir)" config "$config_key")"
+               result="$(__git config "$config_key")"
        fi
 
        echo "$result"
@@ -901,8 +1067,8 @@ __git_whitespacelist="nowarn warn error error-all fix"
 
 _git_am ()
 {
-       local dir="$(__gitdir)"
-       if [ -d "$dir"/rebase-apply ]; then
+       __git_find_repo_path
+       if [ -d "$__git_repo_path"/rebase-apply ]; then
                __gitcomp "--skip --continue --resolved --abort"
                return
        fi
@@ -990,7 +1156,8 @@ _git_bisect ()
        local subcommands="start bad good skip reset visualize replay log run"
        local subcommand="$(__git_find_on_cmdline "$subcommands")"
        if [ -z "$subcommand" ]; then
-               if [ -f "$(__gitdir)"/BISECT_START ]; then
+               __git_find_repo_path
+               if [ -f "$__git_repo_path"/BISECT_START ]; then
                        __gitcomp "$subcommands"
                else
                        __gitcomp "replay start"
@@ -1000,7 +1167,7 @@ _git_bisect ()
 
        case "$subcommand" in
        bad|good|reset|skip|start)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        *)
                ;;
@@ -1022,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
@@ -1035,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
@@ -1080,24 +1247,24 @@ _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 ()
 {
-       local dir="$(__gitdir)"
-       if [ -f "$dir"/CHERRY_PICK_HEAD ]; then
+       __git_find_repo_path
+       if [ -f "$__git_repo_path"/CHERRY_PICK_HEAD ]; then
                __gitcomp "--continue --quit --abort"
                return
        fi
@@ -1106,7 +1273,7 @@ _git_cherry_pick ()
                __gitcomp "--edit --no-commit --signoff --strategy= --mainline"
                ;;
        *)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                ;;
        esac
 }
@@ -1158,7 +1325,7 @@ _git_commit ()
 {
        case "$prev" in
        -c|-C)
-               __gitcomp_nl "$(__git_refs)" "" "${cur}"
+               __git_complete_refs
                return
                ;;
        esac
@@ -1171,7 +1338,7 @@ _git_commit ()
                ;;
        --reuse-message=*|--reedit-message=*|\
        --fixup=*|--squash=*)
-               __gitcomp_nl "$(__git_refs)" "" "${cur#*=}"
+               __git_complete_refs --cur="${cur#*=}"
                return
                ;;
        --untracked-files=*)
@@ -1192,7 +1359,7 @@ _git_commit ()
                return
        esac
 
-       if git rev-parse --verify --quiet HEAD >/dev/null; then
+       if __git rev-parse --verify --quiet HEAD >/dev/null; then
                __git_complete_index_file "--committable"
        else
                # This is the first commit
@@ -1207,10 +1374,11 @@ _git_describe ()
                __gitcomp "
                        --all --tags --contains --abbrev= --candidates=
                        --exact-match --debug --long --match --always --first-parent
+                       --exclude
                        "
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 __git_diff_algorithms="myers minimal patience histogram"
@@ -1361,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 ()
@@ -1392,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 ()
@@ -1500,12 +1700,25 @@ __git_log_date_formats="relative iso8601 rfc2822 short local default raw"
 _git_log ()
 {
        __git_has_doubledash && return
+       __git_find_repo_path
 
-       local g="$(git rev-parse --git-dir 2>/dev/null)"
        local merge=""
-       if [ -f "$g/MERGE_HEAD" ]; then
+       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)
@@ -1551,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
 }
@@ -1573,7 +1801,7 @@ _git_merge ()
                        --rerere-autoupdate --no-rerere-autoupdate --abort --continue"
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_mergetool ()
@@ -1598,7 +1826,7 @@ _git_merge_base ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_mv ()
@@ -1636,7 +1864,7 @@ _git_notes ()
        ,*)
                case "$prev" in
                --ref)
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                        ;;
                *)
                        __gitcomp "$subcommands --ref"
@@ -1645,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=
@@ -1664,7 +1892,7 @@ _git_notes ()
                -m|-F)
                        ;;
                *)
-                       __gitcomp_nl "$(__git_refs)"
+                       __git_complete_refs
                        ;;
                esac
                ;;
@@ -1702,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
 }
@@ -1750,11 +1978,12 @@ _git_push ()
 
 _git_rebase ()
 {
-       local dir="$(__gitdir)"
-       if [ -f "$dir"/rebase-merge/interactive ]; then
+       __git_find_repo_path
+       if [ -f "$__git_repo_path"/rebase-merge/interactive ]; then
                __gitcomp "--continue --skip --abort --quit --edit-todo"
                return
-       elif [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
+       elif [ -d "$__git_repo_path"/rebase-apply ] || \
+            [ -d "$__git_repo_path"/rebase-merge ]; then
                __gitcomp "--continue --skip --abort --quit"
                return
        fi
@@ -1780,7 +2009,7 @@ _git_rebase ()
 
                return
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_reflog ()
@@ -1791,7 +2020,7 @@ _git_reflog ()
        if [ -z "$subcommand" ]; then
                __gitcomp "$subcommands"
        else
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
        fi
 }
 
@@ -1802,9 +2031,7 @@ _git_send_email ()
 {
        case "$prev" in
        --to|--cc|--bcc|--from)
-               __gitcomp "
-               $(git --git-dir="$(__gitdir)" send-email --dump-aliases 2>/dev/null)
-               "
+               __gitcomp "$(__git send-email --dump-aliases)"
                return
                ;;
        esac
@@ -1834,9 +2061,7 @@ _git_send_email ()
                return
                ;;
        --to=*|--cc=*|--bcc=*|--from=*)
-               __gitcomp "
-               $(git --git-dir="$(__gitdir)" send-email --dump-aliases 2>/dev/null)
-               " "" "${cur#--*=}"
+               __gitcomp "$(__git send-email --dump-aliases)" "" "${cur#--*=}"
                return
                ;;
        --*)
@@ -1930,7 +2155,7 @@ __git_config_get_set_variables ()
                c=$((--c))
        done
 
-       git --git-dir="$(__gitdir)" config $config_file --name-only --list 2>/dev/null
+       __git config $config_file --name-only --list
 }
 
 _git_config ()
@@ -1941,7 +2166,7 @@ _git_config ()
                return
                ;;
        branch.*.merge)
-               __gitcomp_nl "$(__git_refs)"
+               __git_complete_refs
                return
                ;;
        branch.*.rebase)
@@ -1965,9 +2190,8 @@ _git_config ()
        remote.*.push)
                local remote="${prev#remote.}"
                remote="${remote%.push}"
-               __gitcomp_nl "$(git --git-dir="$(__gitdir)" \
-                       for-each-ref --format='%(refname):%(refname)' \
-                       refs/heads)"
+               __gitcomp_nl "$(__git for-each-ref \
+                       --format='%(refname):%(refname)' refs/heads)"
                return
                ;;
        pull.twohead|pull.octopus)
@@ -2046,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
                ;;
@@ -2453,7 +2677,7 @@ _git_replace ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_rerere ()
@@ -2477,13 +2701,13 @@ _git_reset ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_revert ()
 {
-       local dir="$(__gitdir)"
-       if [ -f "$dir"/REVERT_HEAD ]; then
+       __git_find_repo_path
+       if [ -f "$__git_repo_path"/REVERT_HEAD ]; then
                __gitcomp "--continue --quit --abort"
                return
        fi
@@ -2496,7 +2720,7 @@ _git_revert ()
                return
                ;;
        esac
-       __gitcomp_nl "$(__git_refs)"
+       __git_complete_refs
 }
 
 _git_rm ()
@@ -2604,14 +2828,14 @@ _git_stash ()
                        ;;
                branch,*)
                        if [ $cword -eq 3 ]; then
-                               __gitcomp_nl "$(__git_refs)";
+                               __git_complete_refs
                        else
-                               __gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
+                               __gitcomp_nl "$(__git stash list \
                                                | sed -n -e 's/:.*//p')"
                        fi
                        ;;
                show,*|apply,*|drop,*|pop,*)
-                       __gitcomp_nl "$(git --git-dir="$(__gitdir)" stash list \
+                       __gitcomp_nl "$(__git stash list \
                                        | sed -n -e 's/:.*//p')"
                        ;;
                *)
@@ -2771,7 +2995,7 @@ _git_tag ()
                i="${words[c]}"
                case "$i" in
                -d|-v)
-                       __gitcomp_nl "$(__git_tags)"
+                       __gitcomp_direct "$(__git_tags "" "$cur" " ")"
                        return
                        ;;
                -f)
@@ -2786,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
 
@@ -2799,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
@@ -2838,7 +3062,8 @@ _git_worktree ()
 
 __git_main ()
 {
-       local i c=1 command __git_dir
+       local i c=1 command __git_dir __git_repo_path
+       local __git_C_args C_args_count=0
 
        while [ $c -lt $cword ]; do
                i="${words[c]}"
@@ -2848,6 +3073,10 @@ __git_main ()
                --bare)      __git_dir="." ;;
                --help) command="help"; break ;;
                -c|--work-tree|--namespace) ((c++)) ;;
+               -C)     __git_C_args[C_args_count++]=-C
+                       ((c++))
+                       __git_C_args[C_args_count++]="${words[c]}"
+                       ;;
                -*) ;;
                *) command="$i"; break ;;
                esac
@@ -2855,6 +3084,17 @@ __git_main ()
        done
 
        if [ -z "$command" ]; then
+               case "$prev" in
+               --git-dir|-C|--work-tree)
+                       # these need a path argument, let's fall back to
+                       # Bash filename completion
+                       return
+                       ;;
+               -c|--namespace)
+                       # we don't support completing these options' arguments
+                       return
+                       ;;
+               esac
                case "$cur" in
                --*)   __gitcomp "
                        --paginate
@@ -2880,13 +3120,13 @@ __git_main ()
        fi
 
        local completion_func="_git_${command//-/_}"
-       declare -f $completion_func >/dev/null && $completion_func && return
+       declare -f $completion_func >/dev/null 2>/dev/null && $completion_func && return
 
        local expansion=$(__git_aliased_command "$command")
        if [ -n "$expansion" ]; then
                words[1]=$expansion
                completion_func="_git_${expansion//-/_}"
-               declare -f $completion_func >/dev/null && $completion_func
+               declare -f $completion_func >/dev/null 2>/dev/null && $completion_func
        fi
 }
 
@@ -2894,9 +3134,11 @@ __gitk_main ()
 {
        __git_has_doubledash && return
 
-       local g="$(__gitdir)"
+       local __git_repo_path
+       __git_find_repo_path
+
        local merge=""
-       if [ -f "$g/MERGE_HEAD" ]; then
+       if [ -f "$__git_repo_path/MERGE_HEAD" ]; then
                merge="--merge"
        fi
        case "$cur" in
@@ -2943,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 4e17e45ed265b3f4861d1b7a1e86edaa4df3b1ac..8d652bf27c9444d3696c4b942dc85f062e2bf53e 100644 (file)
--- a/convert.c
+++ b/convert.c
@@ -1028,7 +1028,7 @@ static int ident_to_worktree(const char *path, const char *src, size_t len,
        return 1;
 }
 
-static enum crlf_action git_path_check_crlf(struct git_attr_check *check)
+static enum crlf_action git_path_check_crlf(struct attr_check_item *check)
 {
        const char *value = check->value;
 
@@ -1045,7 +1045,7 @@ static enum crlf_action git_path_check_crlf(struct git_attr_check *check)
        return CRLF_UNDEFINED;
 }
 
-static enum eol git_path_check_eol(struct git_attr_check *check)
+static enum eol git_path_check_eol(struct attr_check_item *check)
 {
        const char *value = check->value;
 
@@ -1058,7 +1058,7 @@ static enum eol git_path_check_eol(struct git_attr_check *check)
        return EOL_UNSET;
 }
 
-static struct convert_driver *git_path_check_convert(struct git_attr_check *check)
+static struct convert_driver *git_path_check_convert(struct attr_check_item *check)
 {
        const char *value = check->value;
        struct convert_driver *drv;
@@ -1071,7 +1071,7 @@ static struct convert_driver *git_path_check_convert(struct git_attr_check *chec
        return NULL;
 }
 
-static int git_path_check_ident(struct git_attr_check *check)
+static int git_path_check_ident(struct attr_check_item *check)
 {
        const char *value = check->value;
 
@@ -1085,24 +1085,19 @@ struct conv_attrs {
        int ident;
 };
 
-static const char *conv_attr_name[] = {
-       "crlf", "ident", "filter", "eol", "text",
-};
-#define NUM_CONV_ATTRS ARRAY_SIZE(conv_attr_name)
-
 static void convert_attrs(struct conv_attrs *ca, const char *path)
 {
-       int i;
-       static struct git_attr_check ccheck[NUM_CONV_ATTRS];
+       static struct attr_check *check;
 
-       if (!ccheck[0].attr) {
-               for (i = 0; i < NUM_CONV_ATTRS; i++)
-                       ccheck[i].attr = git_attr(conv_attr_name[i]);
+       if (!check) {
+               check = attr_check_initl("crlf", "ident", "filter",
+                                        "eol", "text", NULL);
                user_convert_tail = &user_convert;
                git_config(read_convert_config, NULL);
        }
 
-       if (!git_check_attr(path, NUM_CONV_ATTRS, ccheck)) {
+       if (!git_check_attr(path, check)) {
+               struct attr_check_item *ccheck = check->items;
                ca->crlf_action = git_path_check_crlf(ccheck + 4);
                if (ca->crlf_action == CRLF_UNDEFINED)
                        ca->crlf_action = git_path_check_crlf(ccheck + 0);
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);
 
diff --git a/dir.c b/dir.c
index aeeb5ce10490ef1e0adb907d1e21bcbbbbb401ac..f451bfa48c0a0e7905d4c2adf4e3e05a8d272a8a 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -9,6 +9,7 @@
  */
 #include "cache.h"
 #include "dir.h"
+#include "attr.h"
 #include "refs.h"
 #include "wildmatch.h"
 #include "pathspec.h"
@@ -134,7 +135,8 @@ static size_t common_prefix_len(const struct pathspec *pathspec)
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (n = 0; n < pathspec->nr; n++) {
                size_t i = 0, len = 0, item_len;
@@ -209,6 +211,36 @@ int within_depth(const char *name, int namelen,
 #define DO_MATCH_DIRECTORY (1<<1)
 #define DO_MATCH_SUBMODULE (1<<2)
 
+static int match_attrs(const char *name, int namelen,
+                      const struct pathspec_item *item)
+{
+       int i;
+
+       git_check_attr(name, item->attr_check);
+       for (i = 0; i < item->attr_match_nr; i++) {
+               const char *value;
+               int matched;
+               enum attr_match_mode match_mode;
+
+               value = item->attr_check->items[i].value;
+               match_mode = item->attr_match[i].match_mode;
+
+               if (ATTR_TRUE(value))
+                       matched = (match_mode == MATCH_SET);
+               else if (ATTR_FALSE(value))
+                       matched = (match_mode == MATCH_UNSET);
+               else if (ATTR_UNSET(value))
+                       matched = (match_mode == MATCH_UNSPECIFIED);
+               else
+                       matched = (match_mode == MATCH_VALUE &&
+                                  !strcmp(item->attr_match[i].value, value));
+               if (!matched)
+                       return 0;
+       }
+
+       return 1;
+}
+
 /*
  * Does 'match' match the given name?
  * A match is found if
@@ -261,6 +293,9 @@ static int match_pathspec_item(const struct pathspec_item *item, int prefix,
            strncmp(item->match, name - prefix, item->prefix))
                return 0;
 
+       if (item->attr_match_nr && !match_attrs(name, namelen, item))
+               return 0;
+
        /* If the match was just the prefix, we matched */
        if (!*match)
                return MATCHED_RECURSIVELY;
@@ -339,7 +374,8 @@ static int do_match_pathspec(const struct pathspec *ps,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        if (!ps->nr) {
                if (!ps->recursive ||
@@ -1361,7 +1397,8 @@ static int simplify_away(const char *path, int pathlen,
                       PATHSPEC_LITERAL |
                       PATHSPEC_GLOB |
                       PATHSPEC_ICASE |
-                      PATHSPEC_EXCLUDE);
+                      PATHSPEC_EXCLUDE |
+                      PATHSPEC_ATTR);
 
        for (i = 0; i < pathspec->nr; i++) {
                const struct pathspec_item *item = &pathspec->items[i];
@@ -2728,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 2fdba76222b8f5744f1ae84fd11e72a7a3c115cf..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,18 +285,16 @@ 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(const char *name)
index 4d5a7f58d011545061decbc70e824274091db3e5..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);
@@ -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;
@@ -1752,7 +1751,7 @@ static int update_branch(struct branch *b)
 
        if (is_null_sha1(b->sha1)) {
                if (b->delete)
-                       delete_ref(b->name, NULL, 0);
+                       delete_ref(NULL, b->name, NULL, 0);
                return 0;
        }
        if (read_ref(b->name, old_sha1))
@@ -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 f12bfcdbb12c0bde9271ba45e5d8b6941439efc1..afb8b05024823981be9bedfaa70f7301c6f7076c 100644 (file)
@@ -35,6 +35,7 @@ static const char *alternate_shallow_file;
 #define COMMON_REF     (1U << 2)
 #define SEEN           (1U << 3)
 #define POPPED         (1U << 4)
+#define ALTERNATE      (1U << 5)
 
 static int marked;
 
@@ -67,6 +68,41 @@ static inline void print_verbose(const struct fetch_pack_args *args,
        fputc('\n', stderr);
 }
 
+struct alternate_object_cache {
+       struct object **items;
+       size_t nr, alloc;
+};
+
+static void cache_one_alternate(const char *refname,
+                               const struct object_id *oid,
+                               void *vcache)
+{
+       struct alternate_object_cache *cache = vcache;
+       struct object *obj = parse_object(oid->hash);
+
+       if (!obj || (obj->flags & ALTERNATE))
+               return;
+
+       obj->flags |= ALTERNATE;
+       ALLOC_GROW(cache->items, cache->nr + 1, cache->alloc);
+       cache->items[cache->nr++] = obj;
+}
+
+static void for_each_cached_alternate(void (*cb)(struct object *))
+{
+       static int initialized;
+       static struct alternate_object_cache cache;
+       size_t i;
+
+       if (!initialized) {
+               for_each_alternate_ref(cache_one_alternate, &cache);
+               initialized = 1;
+       }
+
+       for (i = 0; i < cache.nr; i++)
+               cb(cache.items[i]);
+}
+
 static void rev_list_push(struct commit *commit, int mark)
 {
        if (!(commit->object.flags & mark)) {
@@ -240,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);
 }
 
@@ -253,9 +291,9 @@ static void send_request(struct fetch_pack_args *args,
                write_or_die(fd, buf->buf, buf->len);
 }
 
-static void insert_one_alternate_ref(const struct ref *ref, void *unused)
+static void insert_one_alternate_object(struct object *obj)
 {
-       rev_list_insert_ref(NULL, ref->old_oid.hash);
+       rev_list_insert_ref(NULL, obj->oid.hash);
 }
 
 #define INITIAL_FLUSH 16
@@ -298,7 +336,7 @@ static int find_common(struct fetch_pack_args *args,
        marked = 1;
 
        for_each_ref(rev_list_insert_ref_oid, NULL);
-       for_each_alternate_ref(insert_one_alternate_ref, NULL);
+       for_each_cached_alternate(insert_one_alternate_object);
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
@@ -621,9 +659,9 @@ static void filter_refs(struct fetch_pack_args *args,
        *refs = newlist;
 }
 
-static void mark_alternate_complete(const struct ref *ref, void *unused)
+static void mark_alternate_complete(struct object *obj)
 {
-       mark_complete(ref->old_oid.hash);
+       mark_complete(obj->oid.hash);
 }
 
 static int everything_local(struct fetch_pack_args *args,
@@ -659,7 +697,7 @@ static int everything_local(struct fetch_pack_args *args,
 
        if (!args->deepen) {
                for_each_ref(mark_complete_oid, NULL);
-               for_each_alternate_ref(mark_alternate_complete, NULL);
+               for_each_cached_alternate(mark_alternate_complete);
                commit_list_sort_by_date(&complete);
                if (cutoff)
                        mark_recent_complete_commits(args, cutoff);
@@ -766,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",
@@ -979,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;
 
@@ -1002,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;
        }
 
@@ -1024,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) {
@@ -1034,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;
        }
 
@@ -1066,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,
@@ -1074,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 f5c816e273318709e90a9f5e9a47143a3842f8e3..709a5f6ce6fbdb2da14084e94ae9df1db1c3d0a6 100755 (executable)
@@ -275,20 +275,11 @@ sub list_modified {
        my ($only) = @_;
        my (%data, @return);
        my ($add, $del, $adddel, $file);
-       my @tracked = ();
-
-       if (@ARGV) {
-               @tracked = map {
-                       chomp $_;
-                       unquote_path($_);
-               } run_cmd_pipe(qw(git ls-files --), @ARGV);
-               return if (!@tracked);
-       }
 
        my $reference = get_diff_reference($patch_mode_revision);
        for (run_cmd_pipe(qw(git diff-index --cached
                             --numstat --summary), $reference,
-                            '--', @tracked)) {
+                            '--', @ARGV)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        my ($change, $bin);
@@ -313,7 +304,7 @@ sub list_modified {
                }
        }
 
-       for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @tracked)) {
+       for (run_cmd_pipe(qw(git diff-files --numstat --summary --raw --), @ARGV)) {
                if (($add, $del, $file) =
                    /^([-\d]+)  ([-\d]+)        (.*)/) {
                        $file = unquote_path($file);
@@ -1049,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 8a4a3f85e7ba0a658420c477e1af82e804d7fd36..bd04564a69a052ce856f2ba120855541c66fc650 100644 (file)
@@ -884,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 4734094a3f1aaa7d7c767a0740b46dd6abdd6f7a..2c9c0165b5ab0cee8c9369b9bee33277bd8bc5e9 100644 (file)
@@ -1069,6 +1069,10 @@ git_rebase__interactive () {
 
 case "$action" in
 continue)
+       if test ! -d "$rewritten"
+       then
+               exec git rebase--helper ${force_rebase:+--no-ff} --continue
+       fi
        # do we have anything to commit?
        if git diff-index --cached --quiet HEAD --
        then
@@ -1128,6 +1132,10 @@ first and then run 'git rebase --continue' again.")"
 skip)
        git rerere clear
 
+       if test ! -d "$rewritten"
+       then
+               exec git rebase--helper ${force_rebase:+--no-ff} --continue
+       fi
        do_rest
        return 0
        ;;
@@ -1314,6 +1322,11 @@ expand_todo_ids
 test -d "$rewritten" || test -n "$force_rebase" || skip_unnecessary_picks
 
 checkout_onto
+if test -z "$rebase_root" && test ! -d "$rewritten"
+then
+       require_clean_work_tree "rebase"
+       exec git rebase--helper ${force_rebase:+--no-ff} --continue
+fi
 do_rest
 
 }
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 10c284d1aa2273a3dfe5cc39f1d6738830d02462..2fb651b2b8d9d91a130b1cbd11c3c2b6b1cf961b 100755 (executable)
@@ -7,8 +7,11 @@ USAGE="list [<options>]
    or: $dashless drop [-q|--quiet] [<stash>]
    or: $dashless ( pop | apply ) [--index] [-q|--quiet] [<stash>]
    or: $dashless branch <branchname> [<stash>]
-   or: $dashless [save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
-                      [-u|--include-untracked] [-a|--all] [<message>]]
+   or: $dashless save [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+                     [-u|--include-untracked] [-a|--all] [<message>]
+   or: $dashless [push [--patch] [-k|--[no-]keep-index] [-q|--quiet]
+                      [-u|--include-untracked] [-a|--all] [-m <message>]
+                      [-- <pathspec>...]]
    or: $dashless clear"
 
 SUBDIRECTORY_OK=Yes
@@ -33,15 +36,15 @@ else
 fi
 
 no_changes () {
-       git diff-index --quiet --cached HEAD --ignore-submodules -- &&
-       git diff-files --quiet --ignore-submodules &&
+       git diff-index --quiet --cached HEAD --ignore-submodules -- "$@" &&
+       git diff-files --quiet --ignore-submodules -- "$@" &&
        (test -z "$untracked" || test -z "$(untracked_files)")
 }
 
 untracked_files () {
        excl_opt=--exclude-standard
        test "$untracked" = "all" && excl_opt=
-       git ls-files -o -z $excl_opt
+       git ls-files -o -z $excl_opt -- "$@"
 }
 
 clear_stash () {
@@ -56,11 +59,29 @@ clear_stash () {
 }
 
 create_stash () {
-       stash_msg="$1"
-       untracked="$2"
+       stash_msg=
+       untracked=
+       while test $# != 0
+       do
+               case "$1" in
+               -m|--message)
+                       shift
+                       stash_msg=${1?"BUG: create_stash () -m requires an argument"}
+                       ;;
+               -u|--include-untracked)
+                       shift
+                       untracked=${1?"BUG: create_stash () -u requires an argument"}
+                       ;;
+               --)
+                       shift
+                       break
+                       ;;
+               esac
+               shift
+       done
 
        git update-index -q --refresh
-       if no_changes
+       if no_changes "$@"
        then
                exit 0
        fi
@@ -92,7 +113,7 @@ create_stash () {
                # Untracked files are stored by themselves in a parentless commit, for
                # ease of unpacking later.
                u_commit=$(
-                       untracked_files | (
+                       untracked_files "$@" | (
                                GIT_INDEX_FILE="$TMPindex" &&
                                export GIT_INDEX_FILE &&
                                rm -f "$TMPindex" &&
@@ -115,7 +136,7 @@ create_stash () {
                        git read-tree --index-output="$TMPindex" -m $i_tree &&
                        GIT_INDEX_FILE="$TMPindex" &&
                        export GIT_INDEX_FILE &&
-                       git diff-index --name-only -z HEAD -- >"$TMP-stagenames" &&
+                       git diff-index --name-only -z HEAD -- "$@" >"$TMP-stagenames" &&
                        git update-index -z --add --remove --stdin <"$TMP-stagenames" &&
                        git write-tree &&
                        rm -f "$TMPindex"
@@ -129,7 +150,7 @@ create_stash () {
 
                # find out what the user wants
                GIT_INDEX_FILE="$TMP-index" \
-                       git add--interactive --patch=stash -- &&
+                       git add--interactive --patch=stash -- "$@" &&
 
                # state of the working tree
                w_tree=$(GIT_INDEX_FILE="$TMP-index" git write-tree) ||
@@ -189,10 +210,11 @@ store_stash () {
        return $ret
 }
 
-save_stash () {
+push_stash () {
        keep_index=
        patch_mode=
        untracked=
+       stash_msg=
        while test $# != 0
        do
                case "$1" in
@@ -216,6 +238,11 @@ save_stash () {
                -a|--all)
                        untracked=all
                        ;;
+               -m|--message)
+                       shift
+                       test -z ${1+x} && usage
+                       stash_msg=$1
+                       ;;
                --help)
                        show_help
                        ;;
@@ -251,34 +278,45 @@ save_stash () {
                die "$(gettext "Can't use --patch and --include-untracked or --all at the same time")"
        fi
 
-       stash_msg="$*"
+       test -n "$untracked" || git ls-files --error-unmatch -- "$@" >/dev/null || exit 1
 
        git update-index -q --refresh
-       if no_changes
+       if no_changes "$@"
        then
                say "$(gettext "No local changes to save")"
                exit 0
        fi
+
        git reflog exists $ref_stash ||
                clear_stash || die "$(gettext "Cannot initialize stash")"
 
-       create_stash "$stash_msg" $untracked
+       create_stash -m "$stash_msg" -u "$untracked" -- "$@"
        store_stash -m "$stash_msg" -q $w_commit ||
        die "$(gettext "Cannot save the current status")"
        say "$(eval_gettext "Saved working directory and index state \$stash_msg")"
 
        if test -z "$patch_mode"
        then
-               git reset --hard ${GIT_QUIET:+-q}
+               if test $# != 0
+               then
+                       git reset -q -- "$@"
+                       git ls-files -z --modified -- "$@" |
+                       git checkout-index -z --force --stdin
+                       git clean --force -q -d -- "$@"
+               else
+                       git reset --hard -q
+               fi
                test "$untracked" = "all" && CLEAN_X_OPTION=-x || CLEAN_X_OPTION=
                if test -n "$untracked"
                then
-                       git clean --force --quiet -d $CLEAN_X_OPTION
+                       git clean --force --quiet -d $CLEAN_X_OPTION -- "$@"
                fi
 
                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" ||
@@ -286,11 +324,41 @@ save_stash () {
 
                if test "$keep_index" != "t"
                then
-                       git reset
+                       git reset -q -- "$@"
                fi
        fi
 }
 
+save_stash () {
+       push_options=
+       while test $# != 0
+       do
+               case "$1" in
+               --)
+                       shift
+                       break
+                       ;;
+               -*)
+                       # pass all options through to push_stash
+                       push_options="$push_options $1"
+                       ;;
+               *)
+                       break
+                       ;;
+               esac
+               shift
+       done
+
+       stash_msg="$*"
+
+       if test -z "$stash_msg"
+       then
+               push_stash $push_options
+       else
+               push_stash $push_options -m "$stash_msg"
+       fi
+}
+
 have_stash () {
        git rev-parse --verify --quiet $ref_stash >/dev/null
 }
@@ -590,18 +658,21 @@ apply_to_branch () {
        }
 }
 
+test "$1" = "-p" && set "push" "$@"
+
 PARSE_CACHE='--not-parsed'
-# The default command is "save" if nothing but options are given
+# The default command is "push" if nothing but options are given
 seen_non_option=
 for opt
 do
        case "$opt" in
+       --) break ;;
        -*) ;;
        *) seen_non_option=t; break ;;
        esac
 done
 
-test -n "$seen_non_option" || set "save" "$@"
+test -n "$seen_non_option" || set "push" "$@"
 
 # Main command set
 case "$1" in
@@ -617,6 +688,10 @@ save)
        shift
        save_stash "$@"
        ;;
+push)
+       shift
+       push_stash "$@"
+       ;;
 apply)
        shift
        apply_stash "$@"
@@ -627,7 +702,7 @@ clear)
        ;;
 create)
        shift
-       create_stash "$*" && echo "$w_commit"
+       create_stash -m "$*" && echo "$w_commit"
        ;;
 store)
        shift
@@ -648,7 +723,7 @@ branch)
 *)
        case $# in
        0)
-               save_stash &&
+               push_stash &&
                say "$(gettext "(To restore them type \"git stash apply\")")"
                ;;
        *)
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
 }
index fa423647858fede0cf63a27b2c11eb392b639a09..d2404184ba2edc1ac330898ca610ad5e5df8d6c9 100755 (executable)
@@ -1175,10 +1175,10 @@ sub cmd_branch {
        ::_req_svn();
        require SVN::Client;
 
+       my ($config, $baton, undef) = Git::SVN::Ra::prepare_config_once();
        my $ctx = SVN::Client->new(
-               config => SVN::Core::config_get_config(
-                       $Git::SVN::Ra::config_dir
-               ),
+               auth => $baton,
+               config => $config,
                log_msg => sub {
                        ${ $_[0] } = defined $_message
                                ? $_message
diff --git a/git.c b/git.c
index c887272b129968db161a152ce8a91aca4439ca3c..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)
@@ -473,6 +471,7 @@ static struct cmd_struct commands[] = {
        { "pull", cmd_pull, RUN_SETUP | NEED_WORK_TREE },
        { "push", cmd_push, RUN_SETUP },
        { "read-tree", cmd_read_tree, RUN_SETUP | SUPPORT_SUPER_PREFIX},
+       { "rebase--helper", cmd_rebase__helper, RUN_SETUP | NEED_WORK_TREE },
        { "receive-pack", cmd_receive_pack },
        { "reflog", cmd_reflog, RUN_SETUP },
        { "remote", cmd_remote, RUN_SETUP },
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 845b01a874b45314a22e6000bb4e7762c3dd846a..28b44118cbf2c75b26d017a34ecfdf65dbf4c7eb 100644 (file)
--- a/hex.c
+++ b/hex.c
@@ -53,6 +53,14 @@ int get_oid_hex(const char *hex, struct object_id *oid)
        return get_sha1_hex(hex, oid->hash);
 }
 
+int parse_oid_hex(const char *hex, struct object_id *oid, const char **end)
+{
+       int ret = get_oid_hex(hex, oid);
+       if (!ret)
+               *end = hex + GIT_SHA1_HEXSZ;
+       return ret;
+}
+
 char *sha1_to_hex_r(char *buffer, const unsigned char *sha1)
 {
        static const char hex[] = "0123456789abcdef";
@@ -77,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);
 }
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 ad8be42f912b456492ca1704ce067fea4713401a..ac0d4a5d78d956f09aa0082b286babc36f04b593 100644 (file)
@@ -336,15 +336,6 @@ static const struct ll_merge_driver *find_ll_merge_driver(const char *merge_attr
        return &ll_merge_drv[LL_TEXT_MERGE];
 }
 
-static int git_path_check_merge(const char *path, struct git_attr_check check[2])
-{
-       if (!check[0].attr) {
-               check[0].attr = git_attr("merge");
-               check[1].attr = git_attr("conflict-marker-size");
-       }
-       return git_check_attr(path, 2, check);
-}
-
 static void normalize_file(mmfile_t *mm, const char *path)
 {
        struct strbuf strbuf = STRBUF_INIT;
@@ -362,7 +353,7 @@ int ll_merge(mmbuffer_t *result_buf,
             mmfile_t *theirs, const char *their_label,
             const struct ll_merge_options *opts)
 {
-       static struct git_attr_check check[2];
+       static struct attr_check *check;
        static const struct ll_merge_options default_opts;
        const char *ll_driver_name = NULL;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
@@ -376,10 +367,14 @@ int ll_merge(mmbuffer_t *result_buf,
                normalize_file(ours, path);
                normalize_file(theirs, path);
        }
-       if (!git_path_check_merge(path, check)) {
-               ll_driver_name = check[0].value;
-               if (check[1].value) {
-                       marker_size = atoi(check[1].value);
+
+       if (!check)
+               check = attr_check_initl("merge", "conflict-marker-size", NULL);
+
+       if (!git_check_attr(path, check)) {
+               ll_driver_name = check->items[0].value;
+               if (check->items[1].value) {
+                       marker_size = atoi(check->items[1].value);
                        if (marker_size <= 0)
                                marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
                }
@@ -398,13 +393,13 @@ int ll_merge(mmbuffer_t *result_buf,
 
 int ll_merge_marker_size(const char *path)
 {
-       static struct git_attr_check check;
+       static struct attr_check *check;
        int marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
 
-       if (!check.attr)
-               check.attr = git_attr("conflict-marker-size");
-       if (!git_check_attr(path, 1, &check) && check.value) {
-               marker_size = atoi(check.value);
+       if (!check)
+               check = attr_check_initl("conflict-marker-size", NULL);
+       if (!git_check_attr(path, check) && check->items[0].value) {
+               marker_size = atoi(check->items[0].value);
                if (marker_size <= 0)
                        marker_size = DEFAULT_CONFLICT_MARKER_SIZE;
        }
index 8c2415747a2606aefb29fd6e0092c86da3c72589..4618dd04ca1b6bd2bf53481f39851fb4e5d02336 100644 (file)
@@ -332,35 +332,31 @@ void fmt_output_commit(struct strbuf *filename,
        strbuf_release(&subject);
 }
 
+void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt)
+{
+       if (opt->total > 0) {
+               strbuf_addf(sb, "Subject: [%s%s%0*d/%d] ",
+                           opt->subject_prefix,
+                           *opt->subject_prefix ? " " : "",
+                           digits_in_number(opt->total),
+                           opt->nr, opt->total);
+       } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
+               strbuf_addf(sb, "Subject: [%s] ",
+                           opt->subject_prefix);
+       } else {
+               strbuf_addstr(sb, "Subject: ");
+       }
+}
+
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-                            const char **subject_p,
                             const char **extra_headers_p,
                             int *need_8bit_cte_p)
 {
-       const char *subject = NULL;
        const char *extra_headers = opt->extra_headers;
        const char *name = oid_to_hex(opt->zero_commit ?
                                      &null_oid : &commit->object.oid);
 
        *need_8bit_cte_p = 0; /* unknown */
-       if (opt->total > 0) {
-               static char buffer[64];
-               snprintf(buffer, sizeof(buffer),
-                        "Subject: [%s%s%0*d/%d] ",
-                        opt->subject_prefix,
-                        *opt->subject_prefix ? " " : "",
-                        digits_in_number(opt->total),
-                        opt->nr, opt->total);
-               subject = buffer;
-       } else if (opt->total == 0 && opt->subject_prefix && *opt->subject_prefix) {
-               static char buffer[256];
-               snprintf(buffer, sizeof(buffer),
-                        "Subject: [%s] ",
-                        opt->subject_prefix);
-               subject = buffer;
-       } else {
-               subject = "Subject: ";
-       }
 
        fprintf(opt->diffopt.file, "From %s Mon Sep 17 00:00:00 2001\n", name);
        graph_show_oneline(opt->graph);
@@ -417,7 +413,6 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
                opt->diffopt.stat_sep = buffer;
                strbuf_release(&filename);
        }
-       *subject_p = subject;
        *extra_headers_p = extra_headers;
 }
 
@@ -602,8 +597,10 @@ void show_log(struct rev_info *opt)
         */
 
        if (cmit_fmt_is_mail(opt->commit_format)) {
-               log_write_email_headers(opt, commit, &ctx.subject, &extra_headers,
+               log_write_email_headers(opt, commit, &extra_headers,
                                        &ctx.need_8bit_cte);
+               ctx.rev = opt;
+               ctx.print_email_subject = 1;
        } else if (opt->commit_format != CMIT_FMT_USERFORMAT) {
                fputs(diff_get_color_opt(&opt->diffopt, DIFF_COMMIT), opt->diffopt.file);
                if (opt->commit_format != CMIT_FMT_ONELINE)
index c8116e60cde34032da6f2044881d5a4970cd96fe..48f11fb740ddf670cac819cb733cbc736cc30d0e 100644 (file)
@@ -22,7 +22,6 @@ void format_decorations_extended(struct strbuf *sb, const struct commit *commit,
                             format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")")
 void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-                            const char **subject_p,
                             const char **extra_headers_p,
                             int *need_8bit_cte_p);
 void load_ref_decorations(int flags);
@@ -30,5 +29,6 @@ void load_ref_decorations(int flags);
 #define FORMAT_PATCH_NAME_MAX 64
 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
 void fmt_output_subject(struct strbuf *, const char *subject, struct rev_info *);
+void fmt_output_email_subject(struct strbuf *, struct rev_info *);
 
 #endif
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 b7ff1ada3c350dce75ff09709395e97a60c96b96..62decd51cc74a9197abbda28dc8efd6f83287a6b 100644 (file)
@@ -1061,16 +1061,20 @@ static int merge_file_one(struct merge_options *o,
 }
 
 static int handle_change_delete(struct merge_options *o,
-                                const char *path,
+                                const char *path, const char *old_path,
                                 const struct object_id *o_oid, int o_mode,
-                                const struct object_id *a_oid, int a_mode,
-                                const struct object_id *b_oid, int b_mode,
+                                const struct object_id *changed_oid,
+                                int changed_mode,
+                                const char *change_branch,
+                                const char *delete_branch,
                                 const char *change, const char *change_past)
 {
-       char *renamed = NULL;
+       char *alt_path = NULL;
+       const char *update_path = path;
        int ret = 0;
+
        if (dir_in_way(path, !o->call_depth, 0)) {
-               renamed = unique_path(o, path, a_oid ? o->branch1 : o->branch2);
+               update_path = alt_path = unique_path(o, path, change_branch);
        }
 
        if (o->call_depth) {
@@ -1081,43 +1085,43 @@ static int handle_change_delete(struct merge_options *o,
                 */
                ret = remove_file_from_cache(path);
                if (!ret)
-                       ret = update_file(o, 0, o_oid, o_mode,
-                                         renamed ? renamed : path);
-       } else if (!a_oid) {
-               if (!renamed) {
-                       output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
-                              "and %s in %s. Version %s of %s left in tree."),
-                              change, path, o->branch1, change_past,
-                              o->branch2, o->branch2, path);
-                       ret = update_file(o, 0, b_oid, b_mode, path);
-               } else {
-                       output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
-                              "and %s in %s. Version %s of %s left in tree at %s."),
-                              change, path, o->branch1, change_past,
-                              o->branch2, o->branch2, path, renamed);
-                       ret = update_file(o, 0, b_oid, b_mode, renamed);
-               }
+                       ret = update_file(o, 0, o_oid, o_mode, update_path);
        } else {
-               if (!renamed) {
-                       output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
-                              "and %s in %s. Version %s of %s left in tree."),
-                              change, path, o->branch2, change_past,
-                              o->branch1, o->branch1, path);
+               if (!alt_path) {
+                       if (!old_path) {
+                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                                      "and %s in %s. Version %s of %s left in tree."),
+                                      change, path, delete_branch, change_past,
+                                      change_branch, change_branch, path);
+                       } else {
+                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                                      "and %s to %s in %s. Version %s of %s left in tree."),
+                                      change, old_path, delete_branch, change_past, path,
+                                      change_branch, change_branch, path);
+                       }
                } else {
-                       output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
-                              "and %s in %s. Version %s of %s left in tree at %s."),
-                              change, path, o->branch2, change_past,
-                              o->branch1, o->branch1, path, renamed);
-                       ret = update_file(o, 0, a_oid, a_mode, renamed);
+                       if (!old_path) {
+                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                                      "and %s in %s. Version %s of %s left in tree at %s."),
+                                      change, path, delete_branch, change_past,
+                                      change_branch, change_branch, path, alt_path);
+                       } else {
+                               output(o, 1, _("CONFLICT (%s/delete): %s deleted in %s "
+                                      "and %s to %s in %s. Version %s of %s left in tree at %s."),
+                                      change, old_path, delete_branch, change_past, path,
+                                      change_branch, change_branch, path, alt_path);
+                       }
                }
                /*
-                * No need to call update_file() on path when !renamed, since
-                * that would needlessly touch path.  We could call
-                * update_file_flags() with update_cache=0 and update_wd=0,
-                * but that's a no-op.
+                * No need to call update_file() on path when change_branch ==
+                * o->branch1 && !alt_path, since that would needlessly touch
+                * path.  We could call update_file_flags() with update_cache=0
+                * and update_wd=0, but that's a no-op.
                 */
+               if (change_branch != o->branch1 || alt_path)
+                       ret = update_file(o, 0, changed_oid, changed_mode, update_path);
        }
-       free(renamed);
+       free(alt_path);
 
        return ret;
 }
@@ -1125,28 +1129,17 @@ static int handle_change_delete(struct merge_options *o,
 static int conflict_rename_delete(struct merge_options *o,
                                   struct diff_filepair *pair,
                                   const char *rename_branch,
-                                  const char *other_branch)
+                                  const char *delete_branch)
 {
        const struct diff_filespec *orig = pair->one;
        const struct diff_filespec *dest = pair->two;
-       const struct object_id *a_oid = NULL;
-       const struct object_id *b_oid = NULL;
-       int a_mode = 0;
-       int b_mode = 0;
-
-       if (rename_branch == o->branch1) {
-               a_oid = &dest->oid;
-               a_mode = dest->mode;
-       } else {
-               b_oid = &dest->oid;
-               b_mode = dest->mode;
-       }
 
        if (handle_change_delete(o,
                                 o->call_depth ? orig->path : dest->path,
+                                o->call_depth ? NULL : orig->path,
                                 &orig->oid, orig->mode,
-                                a_oid, a_mode,
-                                b_oid, b_mode,
+                                &dest->oid, dest->mode,
+                                rename_branch, delete_branch,
                                 _("rename"), _("renamed")))
                return -1;
 
@@ -1662,11 +1655,27 @@ static int handle_modify_delete(struct merge_options *o,
                                 struct object_id *a_oid, int a_mode,
                                 struct object_id *b_oid, int b_mode)
 {
+       const char *modify_branch, *delete_branch;
+       struct object_id *changed_oid;
+       int changed_mode;
+
+       if (a_oid) {
+               modify_branch = o->branch1;
+               delete_branch = o->branch2;
+               changed_oid = a_oid;
+               changed_mode = a_mode;
+       } else {
+               modify_branch = o->branch2;
+               delete_branch = o->branch1;
+               changed_oid = b_oid;
+               changed_mode = b_mode;
+       }
+
        return handle_change_delete(o,
-                                   path,
+                                   path, NULL,
                                    o_oid, o_mode,
-                                   a_oid, a_mode,
-                                   b_oid, b_mode,
+                                   changed_oid, changed_mode,
+                                   modify_branch, delete_branch,
                                    _("modify"), _("modified"));
 }
 
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 614a0067566733dc91998423f6dc05634cbedc61..f52957dcb34a3340de2d398014ad4605e7620360 100644 (file)
--- a/object.h
+++ b/object.h
@@ -29,7 +29,7 @@ struct object_array {
 /*
  * object flag allocation:
  * revision.h:      0---------10                                26
- * fetch-pack.c:    0---4
+ * fetch-pack.c:    0---5
  * walker.c:        0-2
  * upload-pack.c:       4       11----------------19
  * builtin/blame.c:               12-13
diff --git a/oidset.c b/oidset.c
new file mode 100644 (file)
index 0000000..ac169f0
--- /dev/null
+++ b/oidset.c
@@ -0,0 +1,49 @@
+#include "cache.h"
+#include "oidset.h"
+
+struct oidset_entry {
+       struct hashmap_entry hash;
+       struct object_id oid;
+};
+
+static int oidset_hashcmp(const void *va, const void *vb,
+                         const void *vkey)
+{
+       const struct oidset_entry *a = va, *b = vb;
+       const struct object_id *key = vkey;
+       return oidcmp(&a->oid, key ? key : &b->oid);
+}
+
+int oidset_contains(const struct oidset *set, const struct object_id *oid)
+{
+       struct hashmap_entry key;
+
+       if (!set->map.cmpfn)
+               return 0;
+
+       hashmap_entry_init(&key, sha1hash(oid->hash));
+       return !!hashmap_get(&set->map, &key, oid);
+}
+
+int oidset_insert(struct oidset *set, const struct object_id *oid)
+{
+       struct oidset_entry *entry;
+
+       if (!set->map.cmpfn)
+               hashmap_init(&set->map, oidset_hashcmp, 0);
+
+       if (oidset_contains(set, oid))
+               return 1;
+
+       entry = xmalloc(sizeof(*entry));
+       hashmap_entry_init(&entry->hash, sha1hash(oid->hash));
+       oidcpy(&entry->oid, oid);
+
+       hashmap_add(&set->map, entry);
+       return 0;
+}
+
+void oidset_clear(struct oidset *set)
+{
+       hashmap_free(&set->map, 1);
+}
diff --git a/oidset.h b/oidset.h
new file mode 100644 (file)
index 0000000..b7eaab5
--- /dev/null
+++ b/oidset.h
@@ -0,0 +1,45 @@
+#ifndef OIDSET_H
+#define OIDSET_H
+
+/**
+ * This API is similar to sha1-array, in that it maintains a set of object ids
+ * in a memory-efficient way. The major differences are:
+ *
+ *   1. It uses a hash, so we can do online duplicate removal, rather than
+ *      sort-and-uniq at the end. This can reduce memory footprint if you have
+ *      a large list of oids with many duplicates.
+ *
+ *   2. The per-unique-oid memory footprint is slightly higher due to hash
+ *      table overhead.
+ */
+
+/**
+ * A single oidset; should be zero-initialized (or use OIDSET_INIT).
+ */
+struct oidset {
+       struct hashmap map;
+};
+
+#define OIDSET_INIT { { NULL } }
+
+/**
+ * Returns true iff `set` contains `oid`.
+ */
+int oidset_contains(const struct oidset *set, const struct object_id *oid);
+
+/**
+ * Insert the oid into the set; a copy is made, so "oid" does not need
+ * to persist after this function is called.
+ *
+ * Returns 1 if the oid was already in the set, 0 otherwise. This can be used
+ * to perform an efficient check-and-add.
+ */
+int oidset_insert(struct oidset *set, const struct object_id *oid);
+
+/**
+ * Remove all entries from the oidset, freeing any resources associated with
+ * it.
+ */
+void oidset_clear(struct oidset *set);
+
+#endif /* OIDSET_H */
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 7ababb315944d5536ec251b19937beef5def2538..50f76fff458e45f57ae67c3474de8378f390f978 100644 (file)
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "dir.h"
 #include "pathspec.h"
+#include "attr.h"
 
 /*
  * Finds which of the given pathspecs match items in the index.
@@ -72,6 +73,7 @@ static struct pathspec_magic {
        { PATHSPEC_GLOB,    '\0', "glob" },
        { PATHSPEC_ICASE,   '\0', "icase" },
        { PATHSPEC_EXCLUDE,  '!', "exclude" },
+       { PATHSPEC_ATTR,    '\0', "attr" },
 };
 
 static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
@@ -87,6 +89,116 @@ static void prefix_magic(struct strbuf *sb, int prefixlen, unsigned magic)
        strbuf_addf(sb, ",prefix:%d)", prefixlen);
 }
 
+static size_t strcspn_escaped(const char *s, const char *stop)
+{
+       const char *i;
+
+       for (i = s; *i; i++) {
+               /* skip the escaped character */
+               if (i[0] == '\\' && i[1]) {
+                       i++;
+                       continue;
+               }
+
+               if (strchr(stop, *i))
+                       break;
+       }
+       return i - s;
+}
+
+static inline int invalid_value_char(const char ch)
+{
+       if (isalnum(ch) || strchr(",-_", ch))
+               return 0;
+       return -1;
+}
+
+static char *attr_value_unescape(const char *value)
+{
+       const char *src;
+       char *dst, *ret;
+
+       ret = xmallocz(strlen(value));
+       for (src = value, dst = ret; *src; src++, dst++) {
+               if (*src == '\\') {
+                       if (!src[1])
+                               die(_("Escape character '\\' not allowed as "
+                                     "last character in attr value"));
+                       src++;
+               }
+               if (invalid_value_char(*src))
+                       die("cannot use '%c' for value matching", *src);
+               *dst = *src;
+       }
+       *dst = '\0';
+       return ret;
+}
+
+static void parse_pathspec_attr_match(struct pathspec_item *item, const char *value)
+{
+       struct string_list_item *si;
+       struct string_list list = STRING_LIST_INIT_DUP;
+
+       if (item->attr_check || item->attr_match)
+               die(_("Only one 'attr:' specification is allowed."));
+
+       if (!value || !*value)
+               die(_("attr spec must not be empty"));
+
+       string_list_split(&list, value, ' ', -1);
+       string_list_remove_empty_items(&list, 0);
+
+       item->attr_check = attr_check_alloc();
+       item->attr_match = xcalloc(list.nr, sizeof(struct attr_match));
+
+       for_each_string_list_item(si, &list) {
+               size_t attr_len;
+               char *attr_name;
+               const struct git_attr *a;
+
+               int j = item->attr_match_nr++;
+               const char *attr = si->string;
+               struct attr_match *am = &item->attr_match[j];
+
+               switch (*attr) {
+               case '!':
+                       am->match_mode = MATCH_UNSPECIFIED;
+                       attr++;
+                       attr_len = strlen(attr);
+                       break;
+               case '-':
+                       am->match_mode = MATCH_UNSET;
+                       attr++;
+                       attr_len = strlen(attr);
+                       break;
+               default:
+                       attr_len = strcspn(attr, "=");
+                       if (attr[attr_len] != '=')
+                               am->match_mode = MATCH_SET;
+                       else {
+                               const char *v = &attr[attr_len + 1];
+                               am->match_mode = MATCH_VALUE;
+                               am->value = attr_value_unescape(v);
+                       }
+                       break;
+               }
+
+               attr_name = xmemdupz(attr, attr_len);
+               a = git_attr(attr_name);
+               if (!a)
+                       die(_("invalid attribute name %s"), attr_name);
+
+               attr_check_append(item->attr_check, a);
+
+               free(attr_name);
+       }
+
+       if (item->attr_check->nr != item->attr_match_nr)
+               die("BUG: should have same number of entries");
+
+       string_list_clear(&list, 0);
+}
+
 static inline int get_literal_global(void)
 {
        static int literal = -1;
@@ -164,13 +276,14 @@ static int get_global_magic(int element_magic)
  * returns the position in 'elem' after all magic has been parsed
  */
 static const char *parse_long_magic(unsigned *magic, int *prefix_len,
+                                   struct pathspec_item *item,
                                    const char *elem)
 {
        const char *pos;
        const char *nextat;
 
        for (pos = elem + 2; *pos && *pos != ')'; pos = nextat) {
-               size_t len = strcspn(pos, ",)");
+               size_t len = strcspn_escaped(pos, ",)");
                int i;
 
                if (pos[len] == ',')
@@ -189,6 +302,14 @@ static const char *parse_long_magic(unsigned *magic, int *prefix_len,
                        continue;
                }
 
+               if (starts_with(pos, "attr:")) {
+                       char *attr_body = xmemdupz(pos + 5, len - 5);
+                       parse_pathspec_attr_match(item, attr_body);
+                       *magic |= PATHSPEC_ATTR;
+                       free(attr_body);
+                       continue;
+               }
+
                for (i = 0; i < ARRAY_SIZE(pathspec_magic); i++) {
                        if (strlen(pathspec_magic[i].name) == len &&
                            !strncmp(pathspec_magic[i].name, pos, len)) {
@@ -224,6 +345,12 @@ static const char *parse_short_magic(unsigned *magic, const char *elem)
                char ch = *pos;
                int i;
 
+               /* Special case alias for '!' */
+               if (ch == '^') {
+                       *magic |= PATHSPEC_EXCLUDE;
+                       continue;
+               }
+
                if (!is_pathspec_magic(ch))
                        break;
 
@@ -246,13 +373,14 @@ static const char *parse_short_magic(unsigned *magic, const char *elem)
 }
 
 static const char *parse_element_magic(unsigned *magic, int *prefix_len,
+                                      struct pathspec_item *item,
                                       const char *elem)
 {
        if (elem[0] != ':' || get_literal_global())
                return elem; /* nothing to do */
        else if (elem[1] == '(')
                /* longhand */
-               return parse_long_magic(magic, prefix_len, elem);
+               return parse_long_magic(magic, prefix_len, item, elem);
        else
                /* shorthand */
                return parse_short_magic(magic, elem);
@@ -329,12 +457,17 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
        char *match;
        int pathspec_prefix = -1;
 
+       item->attr_check = NULL;
+       item->attr_match = NULL;
+       item->attr_match_nr = 0;
+
        /* PATHSPEC_LITERAL_PATH ignores magic */
        if (flags & PATHSPEC_LITERAL_PATH) {
                magic = PATHSPEC_LITERAL;
        } else {
                copyfrom = parse_element_magic(&element_magic,
                                               &pathspec_prefix,
+                                              item,
                                               elt);
                magic |= element_magic;
                magic |= get_global_magic(element_magic);
@@ -372,7 +505,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
         * original. Useful for passing to another command.
         */
        if ((flags & PATHSPEC_PREFIX_ORIGIN) &&
-           prefixlen && !get_literal_global()) {
+           !get_literal_global()) {
                struct strbuf sb = STRBUF_INIT;
 
                /* Preserve the actual prefix length of each pattern */
@@ -516,7 +649,7 @@ void parse_pathspec(struct pathspec *pathspec,
        }
 
        pathspec->nr = n;
-       ALLOC_ARRAY(pathspec->items, n);
+       ALLOC_ARRAY(pathspec->items, n + 1);
        item = pathspec->items;
        prefixlen = prefix ? strlen(prefix) : 0;
 
@@ -540,10 +673,15 @@ void parse_pathspec(struct pathspec *pathspec,
                pathspec->magic |= item[i].magic;
        }
 
-       if (nr_exclude == n)
-               die(_("There is nothing to exclude from by :(exclude) patterns.\n"
-                     "Perhaps you forgot to add either ':/' or '.' ?"));
-
+       /*
+        * If everything is an exclude pattern, add one positive pattern
+        * that matches everyting. We allocated an extra one for this.
+        */
+       if (nr_exclude == n) {
+               int plen = (!(flags & PATHSPEC_PREFER_CWD)) ? 0 : prefixlen;
+               init_pathspec_item(item + n, 0, prefix, plen, "");
+               pathspec->nr++;
+       }
 
        if (pathspec->magic & PATHSPEC_MAXDEPTH) {
                if (flags & PATHSPEC_KEEP_ORDER)
@@ -554,26 +692,46 @@ void parse_pathspec(struct pathspec *pathspec,
 
 void copy_pathspec(struct pathspec *dst, const struct pathspec *src)
 {
-       int i;
+       int i, j;
 
        *dst = *src;
        ALLOC_ARRAY(dst->items, dst->nr);
        COPY_ARRAY(dst->items, src->items, dst->nr);
 
        for (i = 0; i < dst->nr; i++) {
-               dst->items[i].match = xstrdup(src->items[i].match);
-               dst->items[i].original = xstrdup(src->items[i].original);
+               struct pathspec_item *d = &dst->items[i];
+               struct pathspec_item *s = &src->items[i];
+
+               d->match = xstrdup(s->match);
+               d->original = xstrdup(s->original);
+
+               ALLOC_ARRAY(d->attr_match, d->attr_match_nr);
+               COPY_ARRAY(d->attr_match, s->attr_match, d->attr_match_nr);
+               for (j = 0; j < d->attr_match_nr; j++) {
+                       const char *value = s->attr_match[j].value;
+                       d->attr_match[j].value = xstrdup_or_null(value);
+               }
+
+               d->attr_check = attr_check_dup(s->attr_check);
        }
 }
 
 void clear_pathspec(struct pathspec *pathspec)
 {
-       int i;
+       int i, j;
 
        for (i = 0; i < pathspec->nr; i++) {
                free(pathspec->items[i].match);
                free(pathspec->items[i].original);
+
+               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);
+
+               if (pathspec->items[i].attr_check)
+                       attr_check_free(pathspec->items[i].attr_check);
        }
+
        free(pathspec->items);
        pathspec->items = NULL;
        pathspec->nr = 0;
index 49fd823ddfe9eb96c983f9f812f6ca1dcf062951..55e976972cecfa5f0c204266c5d2a3a4d3d306a0 100644 (file)
@@ -8,13 +8,15 @@
 #define PATHSPEC_GLOB          (1<<3)
 #define PATHSPEC_ICASE         (1<<4)
 #define PATHSPEC_EXCLUDE       (1<<5)
+#define PATHSPEC_ATTR          (1<<6)
 #define PATHSPEC_ALL_MAGIC       \
        (PATHSPEC_FROMTOP       | \
         PATHSPEC_MAXDEPTH      | \
         PATHSPEC_LITERAL       | \
         PATHSPEC_GLOB          | \
         PATHSPEC_ICASE         | \
-        PATHSPEC_EXCLUDE)
+        PATHSPEC_EXCLUDE       | \
+        PATHSPEC_ATTR)
 
 #define PATHSPEC_ONESTAR 1     /* the pathspec pattern satisfies GFNM_ONESTAR */
 
@@ -31,6 +33,17 @@ struct pathspec {
                int len, prefix;
                int nowildcard_len;
                int flags;
+               int attr_match_nr;
+               struct attr_match {
+                       char *value;
+                       enum attr_match_mode {
+                               MATCH_SET,
+                               MATCH_UNSET,
+                               MATCH_VALUE,
+                               MATCH_UNSPECIFIED
+                       } match_mode;
+               } *attr_match;
+               struct attr_check *attr_check;
        } *items;
 };
 
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 c1fe3a3ef9cba0c155d02475e24315784bd27ae4..70a4c808783bab92b4b6319e3dbd8e16641b1918 100644 (file)
@@ -53,6 +53,8 @@ static void *preload_thread(void *_data)
                        continue;
                if (ce_uptodate(ce))
                        continue;
+               if (ce_skip_worktree(ce))
+                       continue;
                if (!ce_path_match(ce, &p->pathspec, NULL))
                        continue;
                if (threaded_has_symlink_leading_path(&cache, ce->name, ce_namelen(ce)))
index 5e683830d9d66a6a1b50a26ab8c3a8338b33198b..d0f86f5d85cab6c470871cdd6e5ead526259bf6a 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -1607,8 +1607,9 @@ void pp_title_line(struct pretty_print_context *pp,
                                pp->preserve_subject ? "\n" : " ");
 
        strbuf_grow(sb, title.len + 1024);
-       if (pp->subject) {
-               strbuf_addstr(sb, pp->subject);
+       if (pp->print_email_subject) {
+               if (pp->rev)
+                       fmt_output_email_subject(sb, pp->rev);
                if (needs_rfc2047_encoding(title.buf, title.len, RFC2047_SUBJECT))
                        add_rfc2047(sb, title.buf, title.len,
                                                encoding, RFC2047_SUBJECT);
@@ -1818,7 +1819,7 @@ void pretty_print_commit(struct pretty_print_context *pp,
        }
 
        pp_header(pp, encoding, commit, &msg, sb);
-       if (pp->fmt != CMIT_FMT_ONELINE && !pp->subject) {
+       if (pp->fmt != CMIT_FMT_ONELINE && !pp->print_email_subject) {
                strbuf_addch(sb, '\n');
        }
 
index d0199cace4aa0ab22a46fdb5ac2777c4e7e6153b..a8a979bd4fcbac6732d26dda3f826a1eec3d3a6b 100644 (file)
@@ -58,7 +58,7 @@ struct recent_data {
        unsigned long timestamp;
 };
 
-static void add_recent_object(const unsigned char *sha1,
+static void add_recent_object(const struct object_id *oid,
                              unsigned long mtime,
                              struct recent_data *data)
 {
@@ -75,37 +75,37 @@ static void add_recent_object(const unsigned char *sha1,
         * later processing, and the revision machinery expects
         * commits and tags to have been parsed.
         */
-       type = sha1_object_info(sha1, NULL);
+       type = sha1_object_info(oid->hash, NULL);
        if (type < 0)
-               die("unable to get object info for %s", sha1_to_hex(sha1));
+               die("unable to get object info for %s", oid_to_hex(oid));
 
        switch (type) {
        case OBJ_TAG:
        case OBJ_COMMIT:
-               obj = parse_object_or_die(sha1, NULL);
+               obj = parse_object_or_die(oid->hash, NULL);
                break;
        case OBJ_TREE:
-               obj = (struct object *)lookup_tree(sha1);
+               obj = (struct object *)lookup_tree(oid->hash);
                break;
        case OBJ_BLOB:
-               obj = (struct object *)lookup_blob(sha1);
+               obj = (struct object *)lookup_blob(oid->hash);
                break;
        default:
                die("unknown object type for %s: %s",
-                   sha1_to_hex(sha1), typename(type));
+                   oid_to_hex(oid), typename(type));
        }
 
        if (!obj)
-               die("unable to lookup %s", sha1_to_hex(sha1));
+               die("unable to lookup %s", oid_to_hex(oid));
 
        add_pending_object(data->revs, obj, "");
 }
 
-static int add_recent_loose(const unsigned char *sha1,
+static int add_recent_loose(const struct object_id *oid,
                            const char *path, void *data)
 {
        struct stat st;
-       struct object *obj = lookup_object(sha1);
+       struct object *obj = lookup_object(oid->hash);
 
        if (obj && obj->flags & SEEN)
                return 0;
@@ -119,22 +119,22 @@ static int add_recent_loose(const unsigned char *sha1,
                 */
                if (errno == ENOENT)
                        return 0;
-               return error_errno("unable to stat %s", sha1_to_hex(sha1));
+               return error_errno("unable to stat %s", oid_to_hex(oid));
        }
 
-       add_recent_object(sha1, st.st_mtime, data);
+       add_recent_object(oid, st.st_mtime, data);
        return 0;
 }
 
-static int add_recent_packed(const unsigned char *sha1,
+static int add_recent_packed(const struct object_id *oid,
                             struct packed_git *p, uint32_t pos,
                             void *data)
 {
-       struct object *obj = lookup_object(sha1);
+       struct object *obj = lookup_object(oid->hash);
 
        if (obj && obj->flags & SEEN)
                return 0;
-       add_recent_object(sha1, p->mtime, data);
+       add_recent_object(oid, p->mtime, data);
        return 0;
 }
 
index 9054369dd0c6ddfd35794fc63f0e0296ec0c9c91..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);
@@ -1558,10 +1565,27 @@ static void tweak_untracked_cache(struct index_state *istate)
        }
 }
 
+static void tweak_split_index(struct index_state *istate)
+{
+       switch (git_config_get_split_index()) {
+       case -1: /* unset: do nothing */
+               break;
+       case 0: /* false */
+               remove_split_index(istate);
+               break;
+       case 1: /* true */
+               add_split_index(istate);
+               break;
+       default: /* unknown value: do nothing */
+               break;
+       }
+}
+
 static void post_read_index_from(struct index_state *istate)
 {
        check_ce_order(istate);
        tweak_untracked_cache(istate);
+       tweak_split_index(istate);
 }
 
 /* remember to discard_cache() before reading a different cache! */
@@ -1657,10 +1681,25 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
        die("index file corrupt");
 }
 
+/*
+ * Signal that the shared index is used by updating its mtime.
+ *
+ * This way, shared index can be removed if they have not been used
+ * for some time.
+ */
+static void freshen_shared_index(char *base_sha1_hex, int warn)
+{
+       const char *shared_index = git_path("sharedindex.%s", base_sha1_hex);
+       if (!check_and_freshen_file(shared_index, 1) && warn)
+               warning("could not freshen shared index '%s'", shared_index);
+}
+
 int read_index_from(struct index_state *istate, const char *path)
 {
        struct split_index *split_index;
        int ret;
+       char *base_sha1_hex;
+       const char *base_path;
 
        /* istate->initialized covers both .git/index and .git/sharedindex.xxx */
        if (istate->initialized)
@@ -1678,15 +1717,16 @@ int read_index_from(struct index_state *istate, const char *path)
                discard_index(split_index->base);
        else
                split_index->base = xcalloc(1, sizeof(*split_index->base));
-       ret = do_read_index(split_index->base,
-                           git_path("sharedindex.%s",
-                                    sha1_to_hex(split_index->base_sha1)), 1);
+
+       base_sha1_hex = sha1_to_hex(split_index->base_sha1);
+       base_path = git_path("sharedindex.%s", base_sha1_hex);
+       ret = do_read_index(split_index->base, base_path, 1);
        if (hashcmp(split_index->base_sha1, split_index->base->sha1))
                die("broken index, expect %s in %s, got %s",
-                   sha1_to_hex(split_index->base_sha1),
-                   git_path("sharedindex.%s",
-                            sha1_to_hex(split_index->base_sha1)),
+                   base_sha1_hex, base_path,
                    sha1_to_hex(split_index->base->sha1));
+
+       freshen_shared_index(base_sha1_hex, 0);
        merge_base_index(istate);
        post_read_index_from(istate);
        return ret;
@@ -2169,6 +2209,65 @@ static int write_split_index(struct index_state *istate,
        return ret;
 }
 
+static const char *shared_index_expire = "2.weeks.ago";
+
+static unsigned long get_shared_index_expire_date(void)
+{
+       static unsigned long shared_index_expire_date;
+       static int shared_index_expire_date_prepared;
+
+       if (!shared_index_expire_date_prepared) {
+               git_config_get_expiry("splitindex.sharedindexexpire",
+                                     &shared_index_expire);
+               shared_index_expire_date = approxidate(shared_index_expire);
+               shared_index_expire_date_prepared = 1;
+       }
+
+       return shared_index_expire_date;
+}
+
+static int should_delete_shared_index(const char *shared_index_path)
+{
+       struct stat st;
+       unsigned long expiration;
+
+       /* Check timestamp */
+       expiration = get_shared_index_expire_date();
+       if (!expiration)
+               return 0;
+       if (stat(shared_index_path, &st))
+               return error_errno(_("could not stat '%s"), shared_index_path);
+       if (st.st_mtime > expiration)
+               return 0;
+
+       return 1;
+}
+
+static int clean_shared_index_files(const char *current_hex)
+{
+       struct dirent *de;
+       DIR *dir = opendir(get_git_dir());
+
+       if (!dir)
+               return error_errno(_("unable to open git dir: %s"), get_git_dir());
+
+       while ((de = readdir(dir)) != NULL) {
+               const char *sha1_hex;
+               const char *shared_index_path;
+               if (!skip_prefix(de->d_name, "sharedindex.", &sha1_hex))
+                       continue;
+               if (!strcmp(sha1_hex, current_hex))
+                       continue;
+               shared_index_path = git_path("%s", de->d_name);
+               if (should_delete_shared_index(shared_index_path) > 0 &&
+                   unlink(shared_index_path))
+                       warning_errno(_("unable to unlink: %s"), shared_index_path);
+       }
+       closedir(dir);
+
+       return 0;
+}
+
 static struct tempfile temporary_sharedindex;
 
 static int write_shared_index(struct index_state *istate,
@@ -2190,14 +2289,48 @@ static int write_shared_index(struct index_state *istate,
        }
        ret = rename_tempfile(&temporary_sharedindex,
                              git_path("sharedindex.%s", sha1_to_hex(si->base->sha1)));
-       if (!ret)
+       if (!ret) {
                hashcpy(si->base_sha1, si->base->sha1);
+               clean_shared_index_files(sha1_to_hex(si->base->sha1));
+       }
+
        return ret;
 }
 
+static const int default_max_percent_split_change = 20;
+
+static int too_many_not_shared_entries(struct index_state *istate)
+{
+       int i, not_shared = 0;
+       int max_split = git_config_get_max_percent_split_change();
+
+       switch (max_split) {
+       case -1:
+               /* not or badly configured: use the default value */
+               max_split = default_max_percent_split_change;
+               break;
+       case 0:
+               return 1; /* 0% means always write a new shared index */
+       case 100:
+               return 0; /* 100% means never write a new shared index */
+       default:
+               break; /* just use the configured value */
+       }
+
+       /* Count not shared entries */
+       for (i = 0; i < istate->cache_nr; i++) {
+               struct cache_entry *ce = istate->cache[i];
+               if (!ce->index)
+                       not_shared++;
+       }
+
+       return (int64_t)istate->cache_nr * max_split < (int64_t)not_shared * 100;
+}
+
 int write_locked_index(struct index_state *istate, struct lock_file *lock,
                       unsigned flags)
 {
+       int new_shared_index, ret;
        struct split_index *si = istate->split_index;
 
        if (!si || alternate_index_output ||
@@ -2212,13 +2345,24 @@ int write_locked_index(struct index_state *istate, struct lock_file *lock,
                if ((v & 15) < 6)
                        istate->cache_changed |= SPLIT_INDEX_ORDERED;
        }
-       if (istate->cache_changed & SPLIT_INDEX_ORDERED) {
-               int ret = write_shared_index(istate, lock, flags);
+       if (too_many_not_shared_entries(istate))
+               istate->cache_changed |= SPLIT_INDEX_ORDERED;
+
+       new_shared_index = istate->cache_changed & SPLIT_INDEX_ORDERED;
+
+       if (new_shared_index) {
+               ret = write_shared_index(istate, lock, flags);
                if (ret)
                        return ret;
        }
 
-       return write_split_index(istate, lock, flags);
+       ret = write_split_index(istate, lock, flags);
+
+       /* Freshen the shared index only if the split-index was written */
+       if (!ret && !new_shared_index)
+               freshen_shared_index(sha1_to_hex(si->base_sha1), 1);
+
+       return ret;
 }
 
 /*
index 3820b21cc75f5e7fd4b1b1851be0aaa9e3268d7d..3a640448fd83e6cf0bda8ddda081757d5941361e 100644 (file)
 #include "git-compat-util.h"
 #include "version.h"
 #include "trailer.h"
+#include "wt-status.h"
+#include "commit-slab.h"
+
+static struct ref_msg {
+       const char *gone;
+       const char *ahead;
+       const char *behind;
+       const char *ahead_behind;
+} msgs = {
+        /* Untranslated plumbing messages: */
+       "gone",
+       "ahead %d",
+       "behind %d",
+       "ahead %d, behind %d"
+};
+
+void setup_ref_filter_porcelain_msg(void)
+{
+       msgs.gone = _("gone");
+       msgs.ahead = _("ahead %d");
+       msgs.behind = _("behind %d");
+       msgs.ahead_behind = _("ahead %d, behind %d");
+}
 
 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
+typedef enum { COMPARE_EQUAL, COMPARE_UNEQUAL, COMPARE_NONE } cmp_status;
 
 struct align {
        align_type position;
        unsigned int width;
 };
 
+struct if_then_else {
+       cmp_status cmp_status;
+       const char *str;
+       unsigned int then_atom_seen : 1,
+               else_atom_seen : 1,
+               condition_satisfied : 1;
+};
+
+struct refname_atom {
+       enum { R_NORMAL, R_SHORT, R_LSTRIP, R_RSTRIP } option;
+       int lstrip, rstrip;
+};
+
 /*
  * An atom is a valid field atom listed below, possibly prefixed with
  * a "*" to denote deref_tag().
@@ -38,13 +75,24 @@ static struct used_atom {
        union {
                char color[COLOR_MAXLEN];
                struct align align;
-               enum { RR_NORMAL, RR_SHORTEN, RR_TRACK, RR_TRACKSHORT }
-                       remote_ref;
+               struct {
+                       enum { RR_REF, RR_TRACK, RR_TRACKSHORT } option;
+                       struct refname_atom refname;
+                       unsigned int nobracket : 1;
+               } remote_ref;
                struct {
                        enum { C_BARE, C_BODY, C_BODY_DEP, C_LINES, C_SIG, C_SUB, C_TRAILERS } option;
                        unsigned int nlines;
                } contents;
-               enum { O_FULL, O_SHORT } objectname;
+               struct {
+                       cmp_status cmp_status;
+                       const char *str;
+               } if_then_else;
+               struct {
+                       enum { O_FULL, O_LENGTH, O_SHORT } option;
+                       unsigned int length;
+               } objectname;
+               struct refname_atom refname;
        } u;
 } *used_atom;
 static int used_atom_cnt, need_tagged, need_symref;
@@ -58,18 +106,58 @@ static void color_atom_parser(struct used_atom *atom, const char *color_value)
                die(_("unrecognized color: %%(color:%s)"), color_value);
 }
 
-static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
+static void refname_atom_parser_internal(struct refname_atom *atom,
+                                        const char *arg, const char *name)
 {
        if (!arg)
-               atom->u.remote_ref = RR_NORMAL;
+               atom->option = R_NORMAL;
        else if (!strcmp(arg, "short"))
-               atom->u.remote_ref = RR_SHORTEN;
-       else if (!strcmp(arg, "track"))
-               atom->u.remote_ref = RR_TRACK;
-       else if (!strcmp(arg, "trackshort"))
-               atom->u.remote_ref = RR_TRACKSHORT;
-       else
-               die(_("unrecognized format: %%(%s)"), atom->name);
+               atom->option = R_SHORT;
+       else if (skip_prefix(arg, "lstrip=", &arg) ||
+                skip_prefix(arg, "strip=", &arg)) {
+               atom->option = R_LSTRIP;
+               if (strtol_i(arg, 10, &atom->lstrip))
+                       die(_("Integer value expected refname:lstrip=%s"), arg);
+       } else if (skip_prefix(arg, "rstrip=", &arg)) {
+               atom->option = R_RSTRIP;
+               if (strtol_i(arg, 10, &atom->rstrip))
+                       die(_("Integer value expected refname:rstrip=%s"), arg);
+       } else
+               die(_("unrecognized %%(%s) argument: %s"), name, arg);
+}
+
+static void remote_ref_atom_parser(struct used_atom *atom, const char *arg)
+{
+       struct string_list params = STRING_LIST_INIT_DUP;
+       int i;
+
+       if (!arg) {
+               atom->u.remote_ref.option = RR_REF;
+               refname_atom_parser_internal(&atom->u.remote_ref.refname,
+                                            arg, atom->name);
+               return;
+       }
+
+       atom->u.remote_ref.nobracket = 0;
+       string_list_split(&params, arg, ',', -1);
+
+       for (i = 0; i < params.nr; i++) {
+               const char *s = params.items[i].string;
+
+               if (!strcmp(s, "track"))
+                       atom->u.remote_ref.option = RR_TRACK;
+               else if (!strcmp(s, "trackshort"))
+                       atom->u.remote_ref.option = RR_TRACKSHORT;
+               else if (!strcmp(s, "nobracket"))
+                       atom->u.remote_ref.nobracket = 1;
+               else {
+                       atom->u.remote_ref.option = RR_REF;
+                       refname_atom_parser_internal(&atom->u.remote_ref.refname,
+                                                    arg, atom->name);
+               }
+       }
+
+       string_list_clear(&params, 0);
 }
 
 static void body_atom_parser(struct used_atom *atom, const char *arg)
@@ -116,13 +204,25 @@ static void contents_atom_parser(struct used_atom *atom, const char *arg)
 static void objectname_atom_parser(struct used_atom *atom, const char *arg)
 {
        if (!arg)
-               atom->u.objectname = O_FULL;
+               atom->u.objectname.option = O_FULL;
        else if (!strcmp(arg, "short"))
-               atom->u.objectname = O_SHORT;
-       else
+               atom->u.objectname.option = O_SHORT;
+       else if (skip_prefix(arg, "short=", &arg)) {
+               atom->u.objectname.option = O_LENGTH;
+               if (strtoul_ui(arg, 10, &atom->u.objectname.length) ||
+                   atom->u.objectname.length == 0)
+                       die(_("positive value expected objectname:short=%s"), arg);
+               if (atom->u.objectname.length < MINIMUM_ABBREV)
+                       atom->u.objectname.length = MINIMUM_ABBREV;
+       } else
                die(_("unrecognized %%(objectname) argument: %s"), arg);
 }
 
+static void refname_atom_parser(struct used_atom *atom, const char *arg)
+{
+       return refname_atom_parser_internal(&atom->u.refname, arg, atom->name);
+}
+
 static align_type parse_align_position(const char *s)
 {
        if (!strcmp(s, "right"))
@@ -173,12 +273,27 @@ static void align_atom_parser(struct used_atom *atom, const char *arg)
        string_list_clear(&params, 0);
 }
 
+static void if_atom_parser(struct used_atom *atom, const char *arg)
+{
+       if (!arg) {
+               atom->u.if_then_else.cmp_status = COMPARE_NONE;
+               return;
+       } else if (skip_prefix(arg, "equals=", &atom->u.if_then_else.str)) {
+               atom->u.if_then_else.cmp_status = COMPARE_EQUAL;
+       } else if (skip_prefix(arg, "notequals=", &atom->u.if_then_else.str)) {
+               atom->u.if_then_else.cmp_status = COMPARE_UNEQUAL;
+       } else {
+               die(_("unrecognized %%(if) argument: %s"), arg);
+       }
+}
+
+
 static struct {
        const char *name;
        cmp_type cmp_type;
        void (*parser)(struct used_atom *atom, const char *arg);
 } valid_atom[] = {
-       { "refname" },
+       { "refname" , FIELD_STR, refname_atom_parser },
        { "objecttype" },
        { "objectsize", FIELD_ULONG },
        { "objectname", FIELD_STR, objectname_atom_parser },
@@ -208,12 +323,15 @@ static struct {
        { "contents", FIELD_STR, contents_atom_parser },
        { "upstream", FIELD_STR, remote_ref_atom_parser },
        { "push", FIELD_STR, remote_ref_atom_parser },
-       { "symref" },
+       { "symref", FIELD_STR, refname_atom_parser },
        { "flag" },
        { "HEAD" },
        { "color", FIELD_STR, color_atom_parser },
        { "align", FIELD_STR, align_atom_parser },
        { "end" },
+       { "if", FIELD_STR, if_atom_parser },
+       { "then" },
+       { "else" },
 };
 
 #define REF_FORMATTING_STATE_INIT  { 0, NULL }
@@ -221,7 +339,7 @@ static struct {
 struct ref_formatting_stack {
        struct ref_formatting_stack *prev;
        struct strbuf output;
-       void (*at_end)(struct ref_formatting_stack *stack);
+       void (*at_end)(struct ref_formatting_stack **stack);
        void *at_end_data;
 };
 
@@ -232,11 +350,9 @@ struct ref_formatting_state {
 
 struct atom_value {
        const char *s;
-       union {
-               struct align align;
-       } u;
        void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
        unsigned long ul; /* used for sorting when not FIELD_STR */
+       struct used_atom *atom;
 };
 
 /*
@@ -293,7 +409,7 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
                valid_atom[i].parser(&used_atom[at], arg);
        if (*atom == '*')
                need_tagged = 1;
-       if (!strcmp(used_atom[at].name, "symref"))
+       if (!strcmp(valid_atom[i].name, "symref"))
                need_symref = 1;
        return at;
 }
@@ -354,13 +470,14 @@ static void pop_stack_element(struct ref_formatting_stack **stack)
        *stack = prev;
 }
 
-static void end_align_handler(struct ref_formatting_stack *stack)
+static void end_align_handler(struct ref_formatting_stack **stack)
 {
-       struct align *align = (struct align *)stack->at_end_data;
+       struct ref_formatting_stack *cur = *stack;
+       struct align *align = (struct align *)cur->at_end_data;
        struct strbuf s = STRBUF_INIT;
 
-       strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
-       strbuf_swap(&stack->output, &s);
+       strbuf_utf8_align(&s, align->position, align->width, cur->output.buf);
+       strbuf_swap(&cur->output, &s);
        strbuf_release(&s);
 }
 
@@ -371,7 +488,115 @@ static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_s
        push_stack_element(&state->stack);
        new = state->stack;
        new->at_end = end_align_handler;
-       new->at_end_data = &atomv->u.align;
+       new->at_end_data = &atomv->atom->u.align;
+}
+
+static void if_then_else_handler(struct ref_formatting_stack **stack)
+{
+       struct ref_formatting_stack *cur = *stack;
+       struct ref_formatting_stack *prev = cur->prev;
+       struct if_then_else *if_then_else = (struct if_then_else *)cur->at_end_data;
+
+       if (!if_then_else->then_atom_seen)
+               die(_("format: %%(if) atom used without a %%(then) atom"));
+
+       if (if_then_else->else_atom_seen) {
+               /*
+                * There is an %(else) atom: we need to drop one state from the
+                * stack, either the %(else) branch if the condition is satisfied, or
+                * the %(then) branch if it isn't.
+                */
+               if (if_then_else->condition_satisfied) {
+                       strbuf_reset(&cur->output);
+                       pop_stack_element(&cur);
+               } else {
+                       strbuf_swap(&cur->output, &prev->output);
+                       strbuf_reset(&cur->output);
+                       pop_stack_element(&cur);
+               }
+       } else if (!if_then_else->condition_satisfied) {
+               /*
+                * No %(else) atom: just drop the %(then) branch if the
+                * condition is not satisfied.
+                */
+               strbuf_reset(&cur->output);
+       }
+
+       *stack = cur;
+       free(if_then_else);
+}
+
+static void if_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+       struct ref_formatting_stack *new;
+       struct if_then_else *if_then_else = xcalloc(sizeof(struct if_then_else), 1);
+
+       if_then_else->str = atomv->atom->u.if_then_else.str;
+       if_then_else->cmp_status = atomv->atom->u.if_then_else.cmp_status;
+
+       push_stack_element(&state->stack);
+       new = state->stack;
+       new->at_end = if_then_else_handler;
+       new->at_end_data = if_then_else;
+}
+
+static int is_empty(const char *s)
+{
+       while (*s != '\0') {
+               if (!isspace(*s))
+                       return 0;
+               s++;
+       }
+       return 1;
+}
+
+static void then_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+       struct ref_formatting_stack *cur = state->stack;
+       struct if_then_else *if_then_else = NULL;
+
+       if (cur->at_end == if_then_else_handler)
+               if_then_else = (struct if_then_else *)cur->at_end_data;
+       if (!if_then_else)
+               die(_("format: %%(then) atom used without an %%(if) atom"));
+       if (if_then_else->then_atom_seen)
+               die(_("format: %%(then) atom used more than once"));
+       if (if_then_else->else_atom_seen)
+               die(_("format: %%(then) atom used after %%(else)"));
+       if_then_else->then_atom_seen = 1;
+       /*
+        * If the 'equals' or 'notequals' attribute is used then
+        * perform the required comparison. If not, only non-empty
+        * strings satisfy the 'if' condition.
+        */
+       if (if_then_else->cmp_status == COMPARE_EQUAL) {
+               if (!strcmp(if_then_else->str, cur->output.buf))
+                       if_then_else->condition_satisfied = 1;
+       } else if (if_then_else->cmp_status == COMPARE_UNEQUAL) {
+               if (strcmp(if_then_else->str, cur->output.buf))
+                       if_then_else->condition_satisfied = 1;
+       } else if (cur->output.len && !is_empty(cur->output.buf))
+               if_then_else->condition_satisfied = 1;
+       strbuf_reset(&cur->output);
+}
+
+static void else_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+       struct ref_formatting_stack *prev = state->stack;
+       struct if_then_else *if_then_else = NULL;
+
+       if (prev->at_end == if_then_else_handler)
+               if_then_else = (struct if_then_else *)prev->at_end_data;
+       if (!if_then_else)
+               die(_("format: %%(else) atom used without an %%(if) atom"));
+       if (!if_then_else->then_atom_seen)
+               die(_("format: %%(else) atom used without a %%(then) atom"));
+       if (if_then_else->else_atom_seen)
+               die(_("format: %%(else) atom used more than once"));
+       if_then_else->else_atom_seen = 1;
+       push_stack_element(&state->stack);
+       state->stack->at_end_data = prev->at_end_data;
+       state->stack->at_end = prev->at_end;
 }
 
 static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
@@ -381,14 +606,17 @@ static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_sta
 
        if (!current->at_end)
                die(_("format: %%(end) atom used without corresponding atom"));
-       current->at_end(current);
+       current->at_end(&state->stack);
+
+       /*  Stack may have been popped within at_end(), hence reset the current pointer */
+       current = state->stack;
 
        /*
         * Perform quote formatting when the stack element is that of
         * a supporting atom. If nested then perform quote formatting
         * only on the topmost supporting atom.
         */
-       if (!state->stack->prev->prev) {
+       if (!current->prev->prev) {
                quote_formatting(&s, current->output.buf, state->quote_style);
                strbuf_swap(&current->output, &s);
        }
@@ -465,12 +693,15 @@ static int grab_objectname(const char *name, const unsigned char *sha1,
                           struct atom_value *v, struct used_atom *atom)
 {
        if (starts_with(name, "objectname")) {
-               if (atom->u.objectname == O_SHORT) {
+               if (atom->u.objectname.option == O_SHORT) {
                        v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
                        return 1;
-               } else if (atom->u.objectname == O_FULL) {
+               } else if (atom->u.objectname.option == O_FULL) {
                        v->s = xstrdup(sha1_to_hex(sha1));
                        return 1;
+               } else if (atom->u.objectname.option == O_LENGTH) {
+                       v->s = xstrdup(find_unique_abbrev(sha1, atom->u.objectname.length));
+                       return 1;
                } else
                        die("BUG: unknown %%(objectname) option");
        }
@@ -887,50 +1118,108 @@ static inline char *copy_advance(char *dst, const char *src)
        return dst;
 }
 
-static const char *strip_ref_components(const char *refname, const char *nr_arg)
+static const char *lstrip_ref_components(const char *refname, int len)
 {
-       char *end;
-       long nr = strtol(nr_arg, &end, 10);
-       long remaining = nr;
+       long remaining = len;
        const char *start = refname;
 
-       if (nr < 1 || *end != '\0')
-               die(_(":strip= requires a positive integer argument"));
+       if (len < 0) {
+               int i;
+               const char *p = refname;
+
+               /* Find total no of '/' separated path-components */
+               for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
+                       ;
+               /*
+                * The number of components we need to strip is now
+                * the total minus the components to be left (Plus one
+                * because we count the number of '/', but the number
+                * of components is one more than the no of '/').
+                */
+               remaining = i + len + 1;
+       }
 
-       while (remaining) {
+       while (remaining > 0) {
                switch (*start++) {
                case '\0':
-                       die(_("ref '%s' does not have %ld components to :strip"),
-                           refname, nr);
+                       return "";
                case '/':
                        remaining--;
                        break;
                }
        }
+
+       return start;
+}
+
+static const char *rstrip_ref_components(const char *refname, int len)
+{
+       long remaining = len;
+       char *start = xstrdup(refname);
+
+       if (len < 0) {
+               int i;
+               const char *p = refname;
+
+               /* Find total no of '/' separated path-components */
+               for (i = 0; p[i]; p[i] == '/' ? i++ : *p++)
+                       ;
+               /*
+                * The number of components we need to strip is now
+                * the total minus the components to be left (Plus one
+                * because we count the number of '/', but the number
+                * of components is one more than the no of '/').
+                */
+               remaining = i + len + 1;
+       }
+
+       while (remaining-- > 0) {
+               char *p = strrchr(start, '/');
+               if (p == NULL)
+                       return "";
+               else
+                       p[0] = '\0';
+       }
        return start;
 }
 
+static const char *show_ref(struct refname_atom *atom, const char *refname)
+{
+       if (atom->option == R_SHORT)
+               return shorten_unambiguous_ref(refname, warn_ambiguous_refs);
+       else if (atom->option == R_LSTRIP)
+               return lstrip_ref_components(refname, atom->lstrip);
+       else if (atom->option == R_RSTRIP)
+               return rstrip_ref_components(refname, atom->rstrip);
+       else
+               return refname;
+}
+
 static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                                    struct branch *branch, const char **s)
 {
        int num_ours, num_theirs;
-       if (atom->u.remote_ref == RR_SHORTEN)
-               *s = shorten_unambiguous_ref(refname, warn_ambiguous_refs);
-       else if (atom->u.remote_ref == RR_TRACK) {
+       if (atom->u.remote_ref.option == RR_REF)
+               *s = show_ref(&atom->u.remote_ref.refname, refname);
+       else if (atom->u.remote_ref.option == RR_TRACK) {
                if (stat_tracking_info(branch, &num_ours,
-                                      &num_theirs, NULL))
-                       return;
-
-               if (!num_ours && !num_theirs)
+                                      &num_theirs, NULL)) {
+                       *s = xstrdup(msgs.gone);
+               } else if (!num_ours && !num_theirs)
                        *s = "";
                else if (!num_ours)
-                       *s = xstrfmt("[behind %d]", num_theirs);
+                       *s = xstrfmt(msgs.behind, num_theirs);
                else if (!num_theirs)
-                       *s = xstrfmt("[ahead %d]", num_ours);
+                       *s = xstrfmt(msgs.ahead, num_ours);
                else
-                       *s = xstrfmt("[ahead %d, behind %d]",
+                       *s = xstrfmt(msgs.ahead_behind,
                                     num_ours, num_theirs);
-       } else if (atom->u.remote_ref == RR_TRACKSHORT) {
+               if (!atom->u.remote_ref.nobracket && *s[0]) {
+                       const char *to_free = *s;
+                       *s = xstrfmt("[%s]", *s);
+                       free((void *)to_free);
+               }
+       } else if (atom->u.remote_ref.option == RR_TRACKSHORT) {
                if (stat_tracking_info(branch, &num_ours,
                                       &num_theirs, NULL))
                        return;
@@ -943,8 +1232,56 @@ static void fill_remote_ref_details(struct used_atom *atom, const char *refname,
                        *s = ">";
                else
                        *s = "<>";
-       } else /* RR_NORMAL */
-               *s = refname;
+       } else
+               die("BUG: unhandled RR_* enum");
+}
+
+char *get_head_description(void)
+{
+       struct strbuf desc = STRBUF_INIT;
+       struct wt_status_state state;
+       memset(&state, 0, sizeof(state));
+       wt_status_get_state(&state, 1);
+       if (state.rebase_in_progress ||
+           state.rebase_interactive_in_progress)
+               strbuf_addf(&desc, _("(no branch, rebasing %s)"),
+                           state.branch);
+       else if (state.bisect_in_progress)
+               strbuf_addf(&desc, _("(no branch, bisect started on %s)"),
+                           state.branch);
+       else if (state.detached_from) {
+               if (state.detached_at)
+                       /* TRANSLATORS: make sure this matches
+                          "HEAD detached at " in wt-status.c */
+                       strbuf_addf(&desc, _("(HEAD detached at %s)"),
+                               state.detached_from);
+               else
+                       /* TRANSLATORS: make sure this matches
+                          "HEAD detached from " in wt-status.c */
+                       strbuf_addf(&desc, _("(HEAD detached from %s)"),
+                               state.detached_from);
+       }
+       else
+               strbuf_addstr(&desc, _("(no branch)"));
+       free(state.branch);
+       free(state.onto);
+       free(state.detached_from);
+       return strbuf_detach(&desc, NULL);
+}
+
+static const char *get_symref(struct used_atom *atom, struct ref_array_item *ref)
+{
+       if (!ref->symref)
+               return "";
+       else
+               return show_ref(&atom->u.refname, ref->symref);
+}
+
+static const char *get_refname(struct used_atom *atom, struct ref_array_item *ref)
+{
+       if (ref->kind & FILTER_REFS_DETACHED_HEAD)
+               return get_head_description();
+       return show_ref(&atom->u.refname, ref->refname);
 }
 
 /*
@@ -961,9 +1298,9 @@ static void populate_value(struct ref_array_item *ref)
        ref->value = xcalloc(used_atom_cnt, sizeof(struct atom_value));
 
        if (need_symref && (ref->flag & REF_ISSYMREF) && !ref->symref) {
-               unsigned char unused1[20];
+               struct object_id unused1;
                ref->symref = resolve_refdup(ref->refname, RESOLVE_REF_READING,
-                                            unused1, NULL);
+                                            unused1.hash, NULL);
                if (!ref->symref)
                        ref->symref = "";
        }
@@ -975,10 +1312,10 @@ static void populate_value(struct ref_array_item *ref)
                struct atom_value *v = &ref->value[i];
                int deref = 0;
                const char *refname;
-               const char *formatp;
                struct branch *branch = NULL;
 
                v->handler = append_atom;
+               v->atom = atom;
 
                if (*name == '*') {
                        deref = 1;
@@ -986,9 +1323,9 @@ static void populate_value(struct ref_array_item *ref)
                }
 
                if (starts_with(name, "refname"))
-                       refname = ref->refname;
+                       refname = get_refname(atom, ref);
                else if (starts_with(name, "symref"))
-                       refname = ref->symref ? ref->symref : "";
+                       refname = get_symref(atom, ref);
                else if (starts_with(name, "upstream")) {
                        const char *branch_name;
                        /* only local branches may have an upstream */
@@ -1043,30 +1380,27 @@ static void populate_value(struct ref_array_item *ref)
                                v->s = " ";
                        continue;
                } else if (starts_with(name, "align")) {
-                       v->u.align = atom->u.align;
                        v->handler = align_atom_handler;
                        continue;
                } else if (!strcmp(name, "end")) {
                        v->handler = end_atom_handler;
                        continue;
+               } else if (starts_with(name, "if")) {
+                       const char *s;
+
+                       if (skip_prefix(name, "if:", &s))
+                               v->s = xstrdup(s);
+                       v->handler = if_atom_handler;
+                       continue;
+               } else if (!strcmp(name, "then")) {
+                       v->handler = then_atom_handler;
+                       continue;
+               } else if (!strcmp(name, "else")) {
+                       v->handler = else_atom_handler;
+                       continue;
                } else
                        continue;
 
-               formatp = strchr(name, ':');
-               if (formatp) {
-                       const char *arg;
-
-                       formatp++;
-                       if (!strcmp(formatp, "short"))
-                               refname = shorten_unambiguous_ref(refname,
-                                                     warn_ambiguous_refs);
-                       else if (skip_prefix(formatp, "strip=", &arg))
-                               refname = strip_ref_components(refname, arg);
-                       else
-                               die(_("unknown %.*s format %s"),
-                                   (int)(formatp - name), name, formatp);
-               }
-
                if (!deref)
                        v->s = refname;
                else
@@ -1137,10 +1471,23 @@ static void get_ref_atom_value(struct ref_array_item *ref, int atom, struct atom
        *v = &ref->value[atom];
 }
 
+/*
+ * Unknown has to be "0" here, because that's the default value for
+ * contains_cache slab entries that have not yet been assigned.
+ */
 enum contains_result {
-       CONTAINS_UNKNOWN = -1,
-       CONTAINS_NO = 0,
-       CONTAINS_YES = 1
+       CONTAINS_UNKNOWN = 0,
+       CONTAINS_NO,
+       CONTAINS_YES
+};
+
+define_commit_slab(contains_cache, enum contains_result);
+
+struct ref_filter_cbdata {
+       struct ref_array *array;
+       struct ref_filter *filter;
+       struct contains_cache contains_cache;
+       struct contains_cache no_contains_cache;
 };
 
 /*
@@ -1171,24 +1518,24 @@ static int in_commit_list(const struct commit_list *want, struct commit *c)
  * Do not recurse to find out, though, but return -1 if inconclusive.
  */
 static enum contains_result contains_test(struct commit *candidate,
-                           const struct commit_list *want)
+                                         const struct commit_list *want,
+                                         struct contains_cache *cache)
 {
-       /* was it previously marked as containing a want commit? */
-       if (candidate->object.flags & TMP_MARK)
-               return 1;
-       /* or marked as not possibly containing a want commit? */
-       if (candidate->object.flags & UNINTERESTING)
-               return 0;
+       enum contains_result *cached = contains_cache_at(cache, candidate);
+
+       /* If we already have the answer cached, return that. */
+       if (*cached)
+               return *cached;
+
        /* or are we it? */
        if (in_commit_list(want, candidate)) {
-               candidate->object.flags |= TMP_MARK;
-               return 1;
+               *cached = CONTAINS_YES;
+               return CONTAINS_YES;
        }
 
-       if (parse_commit(candidate) < 0)
-               return 0;
-
-       return -1;
+       /* Otherwise, we don't know; prepare to recurse */
+       parse_commit_or_die(candidate);
+       return CONTAINS_UNKNOWN;
 }
 
 static void push_to_contains_stack(struct commit *candidate, struct contains_stack *contains_stack)
@@ -1199,10 +1546,11 @@ static void push_to_contains_stack(struct commit *candidate, struct contains_sta
 }
 
 static enum contains_result contains_tag_algo(struct commit *candidate,
-               const struct commit_list *want)
+                                             const struct commit_list *want,
+                                             struct contains_cache *cache)
 {
        struct contains_stack contains_stack = { 0, 0, NULL };
-       int result = contains_test(candidate, want);
+       enum contains_result result = contains_test(candidate, want, cache);
 
        if (result != CONTAINS_UNKNOWN)
                return result;
@@ -1214,16 +1562,16 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                struct commit_list *parents = entry->parents;
 
                if (!parents) {
-                       commit->object.flags |= UNINTERESTING;
+                       *contains_cache_at(cache, commit) = CONTAINS_NO;
                        contains_stack.nr--;
                }
                /*
                 * If we just popped the stack, parents->item has been marked,
-                * therefore contains_test will return a meaningful 0 or 1.
+                * therefore contains_test will return a meaningful yes/no.
                 */
-               else switch (contains_test(parents->item, want)) {
+               else switch (contains_test(parents->item, want, cache)) {
                case CONTAINS_YES:
-                       commit->object.flags |= TMP_MARK;
+                       *contains_cache_at(cache, commit) = CONTAINS_YES;
                        contains_stack.nr--;
                        break;
                case CONTAINS_NO:
@@ -1235,14 +1583,15 @@ static enum contains_result contains_tag_algo(struct commit *candidate,
                }
        }
        free(contains_stack.contains_stack);
-       return contains_test(candidate, want);
+       return contains_test(candidate, want, cache);
 }
 
-static int commit_contains(struct ref_filter *filter, struct commit *commit)
+static int commit_contains(struct ref_filter *filter, struct commit *commit,
+                          struct commit_list *list, struct contains_cache *cache)
 {
        if (filter->with_commit_tag_algo)
-               return contains_tag_algo(commit, filter->with_commit);
-       return is_descendant_of(commit, filter->with_commit);
+               return contains_tag_algo(commit, list, cache) == CONTAINS_YES;
+       return is_descendant_of(commit, list);
 }
 
 /*
@@ -1329,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;
 }
 
@@ -1424,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;
 
        /*
@@ -1432,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))
+                   !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;
        }
 
@@ -1538,6 +1891,9 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                broken = 1;
        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)
                die("filter_refs: invalid type");
@@ -1560,6 +1916,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
                        head_ref(ref_filter_handler, &ref_cbdata);
        }
 
+       clear_contains_cache(&ref_cbdata.contains_cache);
+       clear_contains_cache(&ref_cbdata.no_contains_cache);
 
        /*  Filters that need revision walking */
        if (filter->merge_commit)
@@ -1635,10 +1993,10 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
        }
 }
 
-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
+void format_ref_array_item(struct ref_array_item *info, const char *format,
+                          int quote_style, struct strbuf *final_buf)
 {
        const char *cp, *sp, *ep;
-       struct strbuf *final_buf;
        struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
 
        state.quote_style = quote_style;
@@ -1668,9 +2026,17 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
        }
        if (state.stack->prev)
                die(_("format: %%(end) atom missing"));
-       final_buf = &state.stack->output;
-       fwrite(final_buf->buf, 1, final_buf->len, stdout);
+       strbuf_addbuf(final_buf, &state.stack->output);
        pop_stack_element(&state.stack);
+}
+
+void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
+{
+       struct strbuf final_buf = STRBUF_INIT;
+
+       format_ref_array_item(info, format, quote_style, &final_buf);
+       fwrite(final_buf.buf, 1, final_buf.len, stdout);
+       strbuf_release(&final_buf);
        putchar('\n');
 }
 
@@ -1725,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 7b05592baf00661a08f973d0a218f7e0afd99453..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,
@@ -71,11 +72,6 @@ struct ref_filter {
                verbose;
 };
 
-struct ref_filter_cbdata {
-       struct ref_array *array;
-       struct ref_filter *filter;
-};
-
 /*  Macros for checking --merged and --no-merged options */
 #define _OPT_MERGED_NO_MERGED(option, filter, h) \
        { OPTION_CALLBACK, 0, option, (filter), N_("commit"), (h), \
@@ -100,6 +96,9 @@ int parse_ref_filter_atom(const char *atom, const char *ep);
 int verify_ref_format(const char *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
 void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
+/*  Based on the given format and quote_style, fill the strbuf */
+void format_ref_array_item(struct ref_array_item *info, const char *format,
+                          int quote_style, struct strbuf *final_buf);
 /*  Print the ref using the given format and quote_style */
 void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);
 /*  Callback function for parsing the sort option */
@@ -108,6 +107,10 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
 struct ref_sorting *ref_default_sorting(void);
 /*  Function to parse --merged and --no-merged options */
 int parse_opt_merge_filter(const struct option *opt, const char *arg, int unset);
+/*  Get the current HEAD's description */
+char *get_head_description(void);
+/*  Set up translated strings in the output. */
+void setup_ref_filter_porcelain_msg(void);
 
 /*
  * Print a single ref, outside of any ref-filter. Note that the
index a246af27678a76d37c87bb56959ac050dff1f97f..99679f58255e386526335da8342e17356b48a4f9 100644 (file)
@@ -10,7 +10,7 @@ struct complete_reflogs {
        char *ref;
        const char *short_ref;
        struct reflog_info {
-               unsigned char osha1[20], nsha1[20];
+               struct object_id ooid, noid;
                char *email;
                unsigned long timestamp;
                int tz;
@@ -19,7 +19,7 @@ struct complete_reflogs {
        int nr, alloc;
 };
 
-static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1,
+static int read_one_reflog(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
@@ -28,8 +28,8 @@ static int read_one_reflog(unsigned char *osha1, unsigned char *nsha1,
 
        ALLOC_GROW(array->items, array->nr + 1, array->alloc);
        item = array->items + array->nr;
-       hashcpy(item->osha1, osha1);
-       hashcpy(item->nsha1, nsha1);
+       oidcpy(&item->ooid, ooid);
+       oidcpy(&item->noid, noid);
        item->email = xstrdup(email);
        item->timestamp = timestamp;
        item->tz = tz;
@@ -45,11 +45,11 @@ static struct complete_reflogs *read_complete_reflog(const char *ref)
        reflogs->ref = xstrdup(ref);
        for_each_reflog_ent(ref, read_one_reflog, reflogs);
        if (reflogs->nr == 0) {
-               unsigned char sha1[20];
+               struct object_id oid;
                const char *name;
                void *name_to_free;
                name = name_to_free = resolve_refdup(ref, RESOLVE_REF_READING,
-                                                    sha1, NULL);
+                                                    oid.hash, NULL);
                if (name) {
                        for_each_reflog_ent(name, read_one_reflog, reflogs);
                        free(name_to_free);
@@ -172,18 +172,18 @@ int add_reflog_for_walk(struct reflog_walk_info *info,
                reflogs = item->util;
        else {
                if (*branch == '\0') {
-                       unsigned char sha1[20];
+                       struct object_id oid;
                        free(branch);
-                       branch = resolve_refdup("HEAD", 0, sha1, NULL);
+                       branch = resolve_refdup("HEAD", 0, oid.hash, NULL);
                        if (!branch)
                                die ("No current branch");
 
                }
                reflogs = read_complete_reflog(branch);
                if (!reflogs || reflogs->nr == 0) {
-                       unsigned char sha1[20];
+                       struct object_id oid;
                        char *b;
-                       if (dwim_log(branch, strlen(branch), sha1, &b) == 1) {
+                       if (dwim_log(branch, strlen(branch), oid.hash, &b) == 1) {
                                if (reflogs) {
                                        free(reflogs->ref);
                                        free(reflogs);
@@ -238,13 +238,13 @@ void fake_reflog_parent(struct reflog_walk_info *info, struct commit *commit)
        do {
                reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
                commit_reflog->recno--;
-               logobj = parse_object(reflog->osha1);
+               logobj = parse_object(reflog->ooid.hash);
        } while (commit_reflog->recno && (logobj && logobj->type != OBJ_COMMIT));
 
-       if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->osha1)) {
+       if (!logobj && commit_reflog->recno >= 0 && is_null_sha1(reflog->ooid.hash)) {
                /* a root commit, but there are still more entries to show */
                reflog = &commit_reflog->reflogs->items[commit_reflog->recno];
-               logobj = parse_object(reflog->nsha1);
+               logobj = parse_object(reflog->noid.hash);
        }
 
        if (!logobj || logobj->type != OBJ_COMMIT) {
diff --git a/refs.c b/refs.c
index 2d717742d603d1bd7f6c1644c72ed2e2fd5ecb2f..a3d5f42e37345096628ab83fee629f22df1bb7b5 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -3,11 +3,13 @@
  */
 
 #include "cache.h"
+#include "hashmap.h"
 #include "lockfile.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
 #include "object.h"
 #include "tag.h"
+#include "submodule.h"
 
 /*
  * List of all available backends
@@ -169,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 */
@@ -183,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);
@@ -284,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)
@@ -365,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[] = {
@@ -428,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;
 }
 
@@ -459,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;
@@ -484,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;
 }
@@ -591,19 +634,23 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
        return 0;
 }
 
-int delete_ref(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, NULL, &err) ||
+                                  flags, msg, &err) ||
            ref_transaction_commit(transaction, &err)) {
                error("%s", err.buf);
                ref_transaction_free(transaction);
@@ -615,6 +662,13 @@ int delete_ref(const char *refname, const unsigned char *old_sha1,
        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;
@@ -674,7 +728,7 @@ struct read_ref_at_cb {
        int *cutoff_cnt;
 };
 
-static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
@@ -698,30 +752,30 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
                 * hold the values for the previous record.
                 */
                if (!is_null_sha1(cb->osha1)) {
-                       hashcpy(cb->sha1, nsha1);
-                       if (hashcmp(cb->osha1, nsha1))
+                       hashcpy(cb->sha1, noid->hash);
+                       if (hashcmp(cb->osha1, noid->hash))
                                warning("Log for ref %s has gap after %s.",
                                        cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
                }
                else if (cb->date == cb->at_time)
-                       hashcpy(cb->sha1, nsha1);
-               else if (hashcmp(nsha1, cb->sha1))
+                       hashcpy(cb->sha1, noid->hash);
+               else if (hashcmp(noid->hash, cb->sha1))
                        warning("Log for ref %s unexpectedly ended on %s.",
                                cb->refname, show_date(cb->date, cb->tz,
                                                       DATE_MODE(RFC2822)));
-               hashcpy(cb->osha1, osha1);
-               hashcpy(cb->nsha1, nsha1);
+               hashcpy(cb->osha1, ooid->hash);
+               hashcpy(cb->nsha1, noid->hash);
                cb->found_it = 1;
                return 1;
        }
-       hashcpy(cb->osha1, osha1);
-       hashcpy(cb->nsha1, nsha1);
+       hashcpy(cb->osha1, ooid->hash);
+       hashcpy(cb->nsha1, noid->hash);
        if (cb->cnt > 0)
                cb->cnt--;
        return 0;
 }
 
-static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
+static int read_ref_at_ent_oldest(struct object_id *ooid, struct object_id *noid,
                                  const char *email, unsigned long timestamp,
                                  int tz, const char *message, void *cb_data)
 {
@@ -735,9 +789,9 @@ static int read_ref_at_ent_oldest(unsigned char *osha1, unsigned char *nsha1,
                *cb->cutoff_tz = tz;
        if (cb->cutoff_cnt)
                *cb->cutoff_cnt = cb->reccnt;
-       hashcpy(cb->sha1, osha1);
+       hashcpy(cb->sha1, ooid->hash);
        if (is_null_sha1(cb->sha1))
-               hashcpy(cb->sha1, nsha1);
+               hashcpy(cb->sha1, noid->hash);
        /* We just want the first entry */
        return 1;
 }
@@ -774,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)
@@ -895,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) ||
@@ -937,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) {
                /*
@@ -1001,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)
@@ -1012,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;
                }
 
@@ -1022,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);
 }
@@ -1118,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);
 
@@ -1166,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)
@@ -1181,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)
@@ -1202,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)
@@ -1222,22 +1317,28 @@ 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 */
-static const char *resolve_ref_recursively(struct ref_store *refs,
-                                          const char *refname,
-                                          int resolve_flags,
-                                          unsigned char *sha1, int *flags)
+const char *refs_resolve_ref_unsafe(struct ref_store *refs,
+                                   const char *refname,
+                                   int resolve_flags,
+                                   unsigned char *sha1, int *flags)
 {
        static struct strbuf sb_refname = STRBUF_INIT;
        int unused_flags;
@@ -1313,7 +1414,7 @@ static 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);
 }
@@ -1321,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);
 }
 
@@ -1342,155 +1443,226 @@ 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;
 }
 
+struct submodule_hash_entry
+{
+       struct hashmap_entry ent; /* must be the first member! */
+
+       struct ref_store *refs;
+
+       /* NUL-terminated name of submodule: */
+       char submodule[FLEX_ARRAY];
+};
+
+static int submodule_hash_cmp(const void *entry, const void *entry_or_key,
+                             const void *keydata)
+{
+       const struct submodule_hash_entry *e1 = entry, *e2 = entry_or_key;
+       const char *submodule = keydata ? keydata : e2->submodule;
+
+       return strcmp(e1->submodule, submodule);
+}
+
+static struct submodule_hash_entry *alloc_submodule_hash_entry(
+               const char *submodule, struct ref_store *refs)
+{
+       struct submodule_hash_entry *entry;
+
+       FLEX_ALLOC_STR(entry, submodule, submodule);
+       hashmap_entry_init(entry, strhash(submodule));
+       entry->refs = refs;
+       return entry;
+}
+
 /* A pointer to the ref_store for the main repository: */
 static struct ref_store *main_ref_store;
 
-/* A linked list of ref_stores for submodules: */
-static struct ref_store *submodule_ref_stores;
+/* A hashmap of ref_stores, stored by submodule name: */
+static struct hashmap submodule_ref_stores;
 
-void base_ref_store_init(struct ref_store *refs,
-                        const struct ref_storage_be *be,
-                        const char *submodule)
+/*
+ * 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_submodule_ref_store(const char *submodule)
 {
-       refs->be = be;
-       if (!submodule) {
-               if (main_ref_store)
-                       die("BUG: main_ref_store initialized twice");
+       struct submodule_hash_entry *entry;
 
-               refs->submodule = "";
-               refs->next = NULL;
-               main_ref_store = refs;
-       } else {
-               if (lookup_ref_store(submodule))
-                       die("BUG: ref_store for submodule '%s' initialized twice",
-                           submodule);
+       if (!submodule_ref_stores.tablesize)
+               /* It's initialized on demand in register_ref_store(). */
+               return NULL;
 
-               refs->submodule = xstrdup(submodule);
-               refs->next = submodule_ref_stores;
-               submodule_ref_stores = refs;
-       }
+       entry = hashmap_get_from_hash(&submodule_ref_stores,
+                                     strhash(submodule), submodule);
+       return entry ? entry->refs : NULL;
 }
 
-struct ref_store *ref_store_init(const char *submodule)
+/*
+ * Create, record, and return a ref_store instance for the specified
+ * gitdir.
+ */
+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);
+       struct ref_store *refs;
 
        if (!be)
                die("BUG: reference backend %s is unknown", be_name);
 
-       if (!submodule || !*submodule)
-               return be->init(NULL);
-       else
-               return be->init(submodule);
+       refs = be->init(gitdir, flags);
+       return refs;
 }
 
-struct ref_store *lookup_ref_store(const char *submodule)
+struct ref_store *get_main_ref_store(void)
 {
-       struct ref_store *refs;
-
-       if (!submodule || !*submodule)
+       if (main_ref_store)
                return main_ref_store;
 
-       for (refs = submodule_ref_stores; refs; refs = refs->next) {
-               if (!strcmp(submodule, refs->submodule))
-                       return refs;
-       }
+       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;
+}
 
-       return NULL;
+/*
+ * 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_ref_store(const char *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;
 }
 
-void assert_main_repository(struct ref_store *refs, const char *caller)
+void base_ref_store_init(struct ref_store *refs,
+                        const struct ref_storage_be *be)
 {
-       if (*refs->submodule)
-               die("BUG: %s called for a submodule", caller);
+       refs->be = be;
 }
 
 /* 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);
@@ -1498,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,
@@ -1544,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 9fbff90e79bfc45fada65651cd01765b17376249..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,8 +332,12 @@ 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 delete_ref(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);
+int delete_ref(const char *msg, const char *refname,
+              const unsigned char *old_sha1, unsigned int flags);
 
 /*
  * Delete the specified references. If there are any problems, emit
@@ -285,24 +345,34 @@ int delete_ref(const char *refname, const unsigned char *old_sha1,
  * 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 */
 typedef int each_reflog_ent_fn(
-               unsigned char *old_sha1, unsigned char *new_sha1,
+               struct object_id *old_oid, struct object_id *new_oid,
                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);
 
 /*
@@ -334,7 +408,8 @@ int create_symref(const char *refname, const char *target, const char *logmsg);
  * $GIT_DIR points to.
  * Return 0 if successful, non-zero otherwise.
  * */
-int set_worktree_head_symref(const char *gitdir, const char *target);
+int set_worktree_head_symref(const char *gitdir, const char *target,
+                            const char *logmsg);
 
 enum action_on_err {
        UPDATE_REFS_MSG_ON_ERR,
@@ -346,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);
 
 /*
@@ -480,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);
@@ -546,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,
@@ -555,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 c041d4ba21a8fe70eb6d8277358e7e3f29a69bfb..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,6 +916,12 @@ struct packed_ref_cache {
  */
 struct files_ref_store {
        struct ref_store base;
+       unsigned int store_flags;
+
+       char *gitdir;
+       char *gitcommondir;
+       char *packed_refs_path;
+
        struct ref_entry *loose;
        struct packed_ref_cache *packed;
 };
@@ -967,34 +977,61 @@ 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, submodule);
+       base_ref_store_init(ref_store, &refs_be_files);
+       refs->store_flags = flags;
+
+       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 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->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;
+
        if (ref_store->be != &refs_be_files)
                die("BUG: ref_store is type \"%s\" not \"files\" in %s",
                    ref_store->be->name, caller);
 
-       if (!submodule_allowed)
-               assert_main_repository(ref_store, caller);
+       refs = (struct files_ref_store *)ref_store;
+
+       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 (struct files_ref_store *)ref_store;
+       return refs;
 }
 
 /* The length of a peeled reference line in packed-refs, including EOL: */
@@ -1125,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->base.submodule)
-               packed_refs_file = git_pathdup_submodule(refs->base.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))
@@ -1156,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;
 }
 
@@ -1201,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->base.submodule)
-               err = strbuf_git_path_submodule(&path, refs->base.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);
@@ -1242,20 +1313,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                                         create_dir_entry(refs, refname.buf,
                                                          refname.len, 1));
                } else {
-                       int read_ok;
-
-                       if (*refs->base.submodule) {
-                               hashclr(sha1);
-                               flag = 0;
-                               read_ok = !resolve_gitlink_ref(refs->base.submodule,
-                                                              refname.buf, sha1);
-                       } else {
-                               read_ok = !read_ref_full(refname.buf,
-                                                        RESOLVE_REF_READING,
-                                                        sha1, &flag);
-                       }
-
-                       if (!read_ok) {
+                       if (!refs_resolve_ref_unsafe(&refs->base,
+                                                    refname.buf,
+                                                    RESOLVE_REF_READING,
+                                                    sha1, &flag)) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
                        } else if (is_null_sha1(sha1)) {
@@ -1344,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;
@@ -1358,10 +1419,7 @@ static int files_read_raw_ref(struct ref_store *ref_store,
        *type = 0;
        strbuf_reset(&sb_path);
 
-       if (*refs->base.submodule)
-               strbuf_git_path_submodule(&sb_path, refs->base.submodule, "%s", refname);
-       else
-               strbuf_git_path(&sb_path, "%s", refname);
+       files_ref_path(refs, &sb_path, refname);
 
        path = sb_path.buf;
 
@@ -1540,7 +1598,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
        int ret = TRANSACTION_GENERIC_ERROR;
 
        assert(err);
-       assert_main_repository(&refs->base, "lock_raw_ref");
+       files_assert_main_repository(refs, "lock_raw_ref");
 
        *type = 0;
 
@@ -1549,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)) {
@@ -1564,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
@@ -1764,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];
 
@@ -1777,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;
 
        /*
@@ -1872,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);
@@ -1945,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);
@@ -1985,6 +2047,13 @@ static int remove_empty_directories(struct strbuf *path)
        return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
 }
 
+static int create_reflock(const char *path, void *cb)
+{
+       struct lock_file *lk = cb;
+
+       return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0;
+}
+
 /*
  * Locks a ref returning the lock on success and NULL on failure.
  * On failure errno is set to something meaningful.
@@ -2000,13 +2069,11 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
        struct strbuf ref_file = STRBUF_INIT;
        struct ref_lock *lock;
        int last_errno = 0;
-       int lflags = LOCK_NO_DEREF;
        int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
        int resolve_flags = RESOLVE_REF_NO_RECURSE;
-       int attempts_remaining = 3;
        int resolved;
 
-       assert_main_repository(&refs->base, "lock_ref_sha1_basic");
+       files_assert_main_repository(refs, "lock_ref_sha1_basic");
        assert(err);
 
        lock = xcalloc(1, sizeof(struct ref_lock));
@@ -2016,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
@@ -2035,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;
@@ -2068,36 +2137,13 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
 
        lock->ref_name = xstrdup(refname);
 
- retry:
-       switch (safe_create_leading_directories_const(ref_file.buf)) {
-       case SCLD_OK:
-               break; /* success */
-       case SCLD_VANISHED:
-               if (--attempts_remaining > 0)
-                       goto retry;
-               /* fall through */
-       default:
+       if (raceproof_create_file(ref_file.buf, create_reflock, lock->lk)) {
                last_errno = errno;
-               strbuf_addf(err, "unable to create directory for '%s'",
-                           ref_file.buf);
+               unable_to_lock_message(ref_file.buf, errno, err);
                goto error_return;
        }
 
-       if (hold_lock_file_for_update(lock->lk, ref_file.buf, lflags) < 0) {
-               last_errno = errno;
-               if (errno == ENOENT && --attempts_remaining > 0)
-                       /*
-                        * Maybe somebody just deleted one of the
-                        * directories leading to ref_file.  Try
-                        * again:
-                        */
-                       goto retry;
-               else {
-                       unable_to_lock_message(ref_file.buf, errno, err);
-                       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;
        }
@@ -2152,7 +2198,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)
        static int timeout_value = 1000;
        struct packed_ref_cache *packed_ref_cache;
 
-       assert_main_repository(&refs->base, "lock_packed_refs");
+       files_assert_main_repository(refs, "lock_packed_refs");
 
        if (!timeout_configured) {
                git_config_get_int("core.packedrefstimeout", &timeout_value);
@@ -2160,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;
        /*
@@ -2190,7 +2236,7 @@ static int commit_packed_refs(struct files_ref_store *refs)
        int save_errno = 0;
        FILE *out;
 
-       assert_main_repository(&refs->base, "commit_packed_refs");
+       files_assert_main_repository(refs, "commit_packed_refs");
 
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
@@ -2223,7 +2269,7 @@ static void rollback_packed_refs(struct files_ref_store *refs)
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(refs);
 
-       assert_main_repository(&refs->base, "rollback_packed_refs");
+       files_assert_main_repository(refs, "rollback_packed_refs");
 
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
@@ -2298,15 +2344,28 @@ static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
        return 0;
 }
 
+enum {
+       REMOVE_EMPTY_PARENTS_REF = 0x01,
+       REMOVE_EMPTY_PARENTS_REFLOG = 0x02
+};
+
 /*
- * Remove empty parents, but spare refs/ and immediate subdirs.
- * Note: munges *name.
+ * Remove empty parent directories associated with the specified
+ * reference and/or its reflog, but spare [logs/]refs/ and immediate
+ * subdirs. flags is a combination of REMOVE_EMPTY_PARENTS_REF and/or
+ * REMOVE_EMPTY_PARENTS_REFLOG.
  */
-static void try_remove_empty_parents(char *name)
+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;
-       p = name;
+
+       strbuf_addstr(&buf, refname);
+       p = buf.buf;
        for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
                while (*p && *p != '/')
                        p++;
@@ -2314,23 +2373,32 @@ static void try_remove_empty_parents(char *name)
                while (*p == '/')
                        p++;
        }
-       for (q = p; *q; q++)
-               ;
-       while (1) {
+       q = buf.buf + buf.len;
+       while (flags & (REMOVE_EMPTY_PARENTS_REF | REMOVE_EMPTY_PARENTS_REFLOG)) {
                while (q > p && *q != '/')
                        q--;
                while (q > p && *(q-1) == '/')
                        q--;
                if (q == p)
                        break;
-               *q = '\0';
-               if (rmdir(git_path("%s", name)))
-                       break;
+               strbuf_setlen(&buf, q - 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;
+
+               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;
@@ -2338,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) ||
@@ -2350,13 +2418,12 @@ static void prune_ref(struct ref_to_prune *r)
        }
        ref_transaction_free(transaction);
        strbuf_release(&err);
-       try_remove_empty_parents(r->name);
 }
 
-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;
        }
 }
@@ -2364,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));
@@ -2379,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;
 }
 
@@ -2397,7 +2465,7 @@ static int repack_without_refs(struct files_ref_store *refs,
        struct string_list_item *refname;
        int ret, needs_repacking = 0, removed = 0;
 
-       assert_main_repository(&refs->base, "repack_without_refs");
+       files_assert_main_repository(refs, "repack_without_refs");
        assert(err);
 
        /* Look for a packed ref */
@@ -2413,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);
@@ -2439,29 +2507,11 @@ static int repack_without_refs(struct files_ref_store *refs,
        return ret;
 }
 
-static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
-{
-       assert(err);
-
-       if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
-               /*
-                * loose.  The loose file name is the same as the
-                * lockfile name, minus ".lock":
-                */
-               char *loose_filename = get_locked_file_path(lock->lk);
-               int res = unlink_or_msg(loose_filename, err);
-               free(loose_filename);
-               if (res)
-                       return 1;
-       }
-       return 0;
-}
-
 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;
 
@@ -2489,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(refname, NULL, flags))
+               if (refs_delete_ref(&refs->base, NULL, refname, NULL, flags))
                        result |= error(_("could not remove reference %s"), refname);
        }
 
@@ -2505,57 +2555,56 @@ 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"
+
+struct rename_cb {
+       const char *tmp_renamed_log;
+       int true_errno;
+};
 
-static int rename_tmp_log(const char *newrefname)
+static int rename_tmp_log_callback(const char *path, void *cb_data)
 {
-       int attempts_remaining = 4;
-       struct strbuf path = STRBUF_INIT;
-       int ret = -1;
+       struct rename_cb *cb = cb_data;
 
- retry:
-       strbuf_reset(&path);
-       strbuf_git_path(&path, "logs/%s", newrefname);
-       switch (safe_create_leading_directories_const(path.buf)) {
-       case SCLD_OK:
-               break; /* success */
-       case SCLD_VANISHED:
-               if (--attempts_remaining > 0)
-                       goto retry;
-               /* fall through */
-       default:
-               error("unable to create directory for %s", newrefname);
-               goto out;
+       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.
+                * Sheesh. Record the true errno for error reporting,
+                * but report EISDIR to raceproof_create_file() so
+                * that it knows to retry.
+                */
+               cb->true_errno = errno;
+               if (errno == ENOTDIR)
+                       errno = EISDIR;
+               return -1;
+       } else {
+               return 0;
        }
+}
 
-       if (rename(git_path(TMP_RENAMED_LOG), path.buf)) {
-               if ((errno==EISDIR || errno==ENOTDIR) && --attempts_remaining > 0) {
-                       /*
-                        * rename(a, b) when b is an existing
-                        * directory ought to result in ISDIR, but
-                        * Solaris 5.8 gives ENOTDIR.  Sheesh.
-                        */
-                       if (remove_empty_directories(&path)) {
-                               error("Directory not empty: logs/%s", newrefname);
-                               goto out;
-                       }
-                       goto retry;
-               } else if (errno == ENOENT && --attempts_remaining > 0) {
-                       /*
-                        * Maybe another process just deleted one of
-                        * the directories in the path to newrefname.
-                        * Try again from the beginning.
-                        */
-                       goto retry;
-               } else {
-                       error("unable to move logfile "TMP_RENAMED_LOG" to logs/%s: %s",
-                               newrefname, strerror(errno));
-                       goto out;
-               }
+static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
+{
+       struct strbuf path = STRBUF_INIT;
+       struct strbuf tmp = STRBUF_INIT;
+       struct rename_cb cb;
+       int ret;
+
+       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.buf);
+               else
+                       error("unable to move logfile %s to %s: %s",
+                             tmp.buf, path.buf,
+                             strerror(cb.true_errno));
        }
-       ret = 0;
-out:
+
        strbuf_release(&path);
+       strbuf_release(&tmp);
        return ret;
 }
 
@@ -2566,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);
 
@@ -2591,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 (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 (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {
+       if (refs_delete_ref(&refs->base, logmsg, oldrefname,
+                           orig_sha1, REF_NODEREF)) {
                error("unable to delete old %s", oldrefname);
                goto rollback;
        }
@@ -2628,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(newrefname, NULL, REF_NODEREF)) {
-               if (errno==EISDIR) {
+       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);
 
@@ -2649,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;
@@ -2670,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,
@@ -2691,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)
@@ -2740,66 +2817,93 @@ static int commit_ref(struct ref_lock *lock)
        return 0;
 }
 
+static int open_or_create_logfile(const char *path, void *cb)
+{
+       int *fd = cb;
+
+       *fd = open(path, O_APPEND | O_WRONLY | O_CREAT, 0666);
+       return (*fd < 0) ? -1 : 0;
+}
+
 /*
- * Create a reflog for a ref.  If force_create = 0, the reflog will
- * only be created for certain refs (those for which
- * should_autocreate_reflog returns non-zero.  Otherwise, create it
- * regardless of the ref name.  Fill in *err and return -1 on failure.
+ * Create a reflog for a ref. If force_create = 0, only create the
+ * reflog for certain refs (those for which should_autocreate_reflog
+ * returns non-zero). Otherwise, create it regardless of the reference
+ * name. If the logfile already existed or was created, return 0 and
+ * set *logfd to the file descriptor opened for appending to the file.
+ * If no logfile exists and we decided not to create one, return 0 and
+ * set *logfd to -1. On failure, fill in *err, set *logfd to -1, and
+ * return -1.
  */
-static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
+static int log_ref_setup(struct files_ref_store *refs,
+                        const char *refname, int force_create,
+                        int *logfd, struct strbuf *err)
 {
-       int logfd, oflags = O_APPEND | O_WRONLY;
+       struct strbuf logfile_sb = STRBUF_INIT;
+       char *logfile;
 
-       strbuf_git_path(logfile, "logs/%s", refname);
-       if (force_create || should_autocreate_reflog(refname)) {
-               if (safe_create_leading_directories(logfile->buf) < 0) {
-                       strbuf_addf(err, "unable to create directory for '%s': "
-                                   "%s", logfile->buf, strerror(errno));
-                       return -1;
-               }
-               oflags |= O_CREAT;
-       }
+       files_reflog_path(refs, &logfile_sb, refname);
+       logfile = strbuf_detach(&logfile_sb, NULL);
 
-       logfd = open(logfile->buf, oflags, 0666);
-       if (logfd < 0) {
-               if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
-                       return 0;
+       if (force_create || should_autocreate_reflog(refname)) {
+               if (raceproof_create_file(logfile, open_or_create_logfile, logfd)) {
+                       if (errno == ENOENT)
+                               strbuf_addf(err, "unable to create directory for '%s': "
+                                           "%s", logfile, strerror(errno));
+                       else if (errno == EISDIR)
+                               strbuf_addf(err, "there are still logs under '%s'",
+                                           logfile);
+                       else
+                               strbuf_addf(err, "unable to append to '%s': %s",
+                                           logfile, strerror(errno));
 
-               if (errno == EISDIR) {
-                       if (remove_empty_directories(logfile)) {
-                               strbuf_addf(err, "there are still logs under "
-                                           "'%s'", logfile->buf);
-                               return -1;
-                       }
-                       logfd = open(logfile->buf, oflags, 0666);
+                       goto error;
                }
-
-               if (logfd < 0) {
-                       strbuf_addf(err, "unable to append to '%s': %s",
-                                   logfile->buf, strerror(errno));
-                       return -1;
+       } else {
+               *logfd = open(logfile, O_APPEND | O_WRONLY, 0666);
+               if (*logfd < 0) {
+                       if (errno == ENOENT || errno == EISDIR) {
+                               /*
+                                * The logfile doesn't already exist,
+                                * but that is not an error; it only
+                                * means that we won't write log
+                                * entries to it.
+                                */
+                               ;
+                       } else {
+                               strbuf_addf(err, "unable to append to '%s': %s",
+                                           logfile, strerror(errno));
+                               goto error;
+                       }
                }
        }
 
-       adjust_shared_perm(logfile->buf);
-       close(logfd);
+       if (*logfd >= 0)
+               adjust_shared_perm(logfile);
+
+       free(logfile);
        return 0;
-}
 
+error:
+       free(logfile);
+       return -1;
+}
 
 static int files_create_reflog(struct ref_store *ref_store,
                               const char *refname, int force_create,
                               struct strbuf *err)
 {
-       int ret;
-       struct strbuf sb = STRBUF_INIT;
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_WRITE, "create_reflog");
+       int fd;
+
+       if (log_ref_setup(refs, refname, force_create, &fd, err))
+               return -1;
 
-       /* Check validity (but we don't need the result): */
-       files_downcast(ref_store, 0, "create_reflog");
+       if (fd >= 0)
+               close(fd);
 
-       ret = log_ref_setup(refname, &sb, err, force_create);
-       strbuf_release(&sb);
-       return ret;
+       return 0;
 }
 
 static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
@@ -2828,59 +2932,51 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
        return 0;
 }
 
-static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
-                          const unsigned char *new_sha1, const char *msg,
-                          struct strbuf *logfile, 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, oflags = O_APPEND | O_WRONLY;
+       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, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
+       result = log_ref_setup(refs, refname,
+                              flags & REF_FORCE_CREATE_REFLOG,
+                              &logfd, err);
 
        if (result)
                return result;
 
-       logfd = open(logfile->buf, oflags);
        if (logfd < 0)
                return 0;
        result = log_ref_write_fd(logfd, old_sha1, new_sha1,
                                  git_committer_info(0), msg);
        if (result) {
-               strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
-                           strerror(errno));
+               struct strbuf sb = STRBUF_INIT;
+               int save_errno = errno;
+
+               files_reflog_path(refs, &sb, refname);
+               strbuf_addf(err, "unable to append to '%s': %s",
+                           sb.buf, strerror(save_errno));
+               strbuf_release(&sb);
                close(logfd);
                return -1;
        }
        if (close(logfd)) {
-               strbuf_addf(err, "unable to append to '%s': %s", logfile->buf,
-                           strerror(errno));
+               struct strbuf sb = STRBUF_INIT;
+               int save_errno = errno;
+
+               files_reflog_path(refs, &sb, refname);
+               strbuf_addf(err, "unable to append to '%s': %s",
+                           sb.buf, strerror(save_errno));
+               strbuf_release(&sb);
                return -1;
        }
        return 0;
 }
 
-static int log_ref_write(const char *refname, const unsigned char *old_sha1,
-                        const unsigned char *new_sha1, const char *msg,
-                        int flags, struct strbuf *err)
-{
-       return files_log_ref_write(refname, old_sha1, new_sha1, msg, flags,
-                                  err);
-}
-
-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)
-{
-       struct strbuf sb = STRBUF_INIT;
-       int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
-                                 err);
-       strbuf_release(&sb);
-       return ret;
-}
-
 /*
  * Write sha1 into the open lockfile, then close the lockfile. On
  * errors, rollback the lockfile, fill in *err and
@@ -2930,10 +3026,12 @@ static int commit_ref_update(struct files_ref_store *refs,
                             const unsigned char *sha1, const char *logmsg,
                             struct strbuf *err)
 {
-       assert_main_repository(&refs->base, "commit_ref_update");
+       files_assert_main_repository(refs, "commit_ref_update");
 
        clear_loose_ref_cache(refs);
-       if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, 0, err)) {
+       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",
                            lock->ref_name, old_msg);
@@ -2959,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 (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);
                        }
@@ -2997,23 +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) &&
-           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;
        }
 
@@ -3021,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);
@@ -3036,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;
@@ -3050,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)
+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;
@@ -3083,7 +3197,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)
        lock->lk = &head_lock;
        lock->ref_name = xstrdup(head_rel);
 
-       ret = create_symref_locked(lock, head_rel, target, NULL);
+       ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
 
        unlock_ref(lock); /* will free lock */
        strbuf_release(&head_path);
@@ -3093,36 +3207,45 @@ int set_worktree_head_symref(const char *gitdir, const char *target)
 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)
 {
-       unsigned char osha1[20], nsha1[20];
+       struct object_id ooid, noid;
        char *email_end, *message;
        unsigned long timestamp;
        int tz;
+       const char *p = sb->buf;
 
        /* old SP new SP name <email> SP time TAB msg LF */
-       if (sb->len < 83 || sb->buf[sb->len - 1] != '\n' ||
-           get_sha1_hex(sb->buf, osha1) || sb->buf[40] != ' ' ||
-           get_sha1_hex(sb->buf + 41, nsha1) || sb->buf[81] != ' ' ||
-           !(email_end = strchr(sb->buf + 82, '>')) ||
+       if (!sb->len || sb->buf[sb->len - 1] != '\n' ||
+           parse_oid_hex(p, &ooid, &p) || *p++ != ' ' ||
+           parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
+           !(email_end = strchr(p, '>')) ||
            email_end[1] != ' ' ||
            !(timestamp = strtoul(email_end + 2, &message, 10)) ||
            !message || message[0] != ' ' ||
@@ -3136,7 +3259,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
                message += 6;
        else
                message += 7;
-       return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data);
+       return fn(&ooid, &noid, p, timestamp, tz, message, cb_data);
 }
 
 static char *find_beginning_of_line(char *bob, char *scan)
@@ -3155,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;
@@ -3180,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;
@@ -3262,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;
 
@@ -3283,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;
 };
@@ -3304,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;
                }
@@ -3349,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;
 }
 
@@ -3560,7 +3697,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
        int ret;
        struct ref_lock *lock;
 
-       assert_main_repository(&refs->base, "lock_ref_for_update");
+       files_assert_main_repository(refs, "lock_ref_for_update");
 
        if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
                update->flags |= REF_DELETING;
@@ -3595,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",
@@ -3686,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;
@@ -3694,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);
 
@@ -3748,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);
@@ -3778,9 +3919,12 @@ static int files_transaction_commit(struct ref_store *ref_store,
 
                if (update->flags & REF_NEEDS_COMMIT ||
                    update->flags & REF_LOG_ONLY) {
-                       if (log_ref_write(lock->ref_name, lock->old_oid.hash,
-                                         update->new_sha1,
-                                         update->msg, update->flags, err)) {
+                       if (files_log_ref_write(refs,
+                                               lock->ref_name,
+                                               lock->old_oid.hash,
+                                               update->new_sha1,
+                                               update->msg, update->flags,
+                                               err)) {
                                char *old_msg = strbuf_detach(err, NULL);
 
                                strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -3810,9 +3954,16 @@ static int files_transaction_commit(struct ref_store *ref_store,
 
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY)) {
-                       if (delete_ref_loose(lock, update->type, err)) {
-                               ret = TRANSACTION_GENERIC_ERROR;
-                               goto cleanup;
+                       if (!(update->type & REF_ISPACKED) ||
+                           update->type & REF_ISSYMREF) {
+                               /* It is a loose reference. */
+                               strbuf_reset(&sb);
+                               files_ref_path(refs, &sb, lock->ref_name);
+                               if (unlink_or_msg(sb.buf, err)) {
+                                       ret = TRANSACTION_GENERIC_ERROR;
+                                       goto cleanup;
+                               }
+                               update->flags |= REF_DELETED_LOOSE;
                        }
 
                        if (!(update->flags & REF_ISPRUNING))
@@ -3825,16 +3976,41 @@ static int files_transaction_commit(struct ref_store *ref_store,
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
-       for_each_string_list_item(ref_to_delete, &refs_to_delete)
-               unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
+
+       /* Delete the reflogs of any references that were deleted: */
+       for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+               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++)
-               if (transaction->updates[i]->backend_data)
-                       unlock_ref(transaction->updates[i]->backend_data);
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               struct ref_lock *lock = update->backend_data;
+
+               if (lock)
+                       unlock_ref(lock);
+
+               if (update->flags & REF_DELETED_LOOSE) {
+                       /*
+                        * The loose reference was deleted. Delete any
+                        * empty parent directories. (Note that this
+                        * can only work because we have already
+                        * removed the lockfile.)
+                        */
+                       try_remove_empty_parents(refs, update->refname,
+                                                REMOVE_EMPTY_PARENTS_REF);
+               }
+       }
+
        string_list_clear(&refs_to_delete, 0);
        free(head_ref);
        string_list_clear(&affected_refnames, 0);
@@ -3855,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;
 
@@ -3886,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++) {
@@ -3895,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;
                }
@@ -3936,10 +4114,10 @@ struct expire_reflog_cb {
        reflog_expiry_should_prune_fn *should_prune_fn;
        void *policy_cb;
        FILE *newlog;
-       unsigned char last_kept_sha1[20];
+       struct object_id last_kept_oid;
 };
 
-static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
                             const char *email, unsigned long timestamp, int tz,
                             const char *message, void *cb_data)
 {
@@ -3947,9 +4125,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
        struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
 
        if (cb->flags & EXPIRE_REFLOGS_REWRITE)
-               osha1 = cb->last_kept_sha1;
+               ooid = &cb->last_kept_oid;
 
-       if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz,
+       if ((*cb->should_prune_fn)(ooid->hash, noid->hash, email, timestamp, tz,
                                   message, policy_cb)) {
                if (!cb->newlog)
                        printf("would prune %s", message);
@@ -3958,9 +4136,9 @@ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
        } else {
                if (cb->newlog) {
                        fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
-                               sha1_to_hex(osha1), sha1_to_hex(nsha1),
+                               oid_to_hex(ooid), oid_to_hex(noid),
                                email, timestamp, tz, message);
-                       hashcpy(cb->last_kept_sha1, nsha1);
+                       oidcpy(&cb->last_kept_oid, noid);
                }
                if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
                        printf("keep %s", message);
@@ -3977,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;
@@ -4004,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
@@ -4034,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)) {
@@ -4047,14 +4227,14 @@ static int files_reflog_expire(struct ref_store *ref_store,
                 */
                int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) &&
                        !(type & REF_ISSYMREF) &&
-                       !is_null_sha1(cb.last_kept_sha1);
+                       !is_null_oid(&cb.last_kept_oid);
 
                if (close_lock_file(&reflog_lock)) {
                        status |= error("couldn't write %s: %s", log_file,
                                        strerror(errno));
                } else if (update &&
                           (write_in_full(get_lock_file_fd(lock->lk),
-                               sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+                               oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) != GIT_SHA1_HEXSZ ||
                            write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
                            close_ref(lock) < 0)) {
                        status |= error("couldn't write %s",
@@ -4080,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 25444cf5b0f6448e4a1ada56d48f09ab3017a759..690498698e41b75cf7a194ac03cafd778f0f19f1 100644 (file)
  */
 #define REF_UPDATE_VIA_HEAD 0x100
 
+/*
+ * Used as a flag in ref_update::flags when the loose reference has
+ * been deleted.
+ */
+#define REF_DELETED_LOOSE 0x200
+
 /*
  * Return true iff refname is minimally safe. "Safe" here means that
  * deleting a loose reference by this name will not do any damage, for
  * This function does not check that the reference name is legal; for
  * that, use check_refname_format().
  *
- * We consider a refname that starts with "refs/" to be safe as long
- * as any ".." components that it might contain do not escape "refs/".
- * Names that do not start with "refs/" are considered safe iff they
- * consist entirely of upper case characters and '_' (like "HEAD" and
- * "MERGE_HEAD" but not "config" or "FOO/BAR").
+ * A refname that starts with "refs/" is considered safe iff it
+ * doesn't contain any "." or ".." components or consecutive '/'
+ * characters, end with '/', or (on Windows) contain any '\'
+ * characters. Names that do not start with "refs/" are considered
+ * safe iff they consist entirely of upper case characters and '_'
+ * (like "HEAD" and "MERGE_HEAD" but not "config" or "FOO/BAR").
  */
 int refname_is_safe(const char *refname);
 
@@ -104,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,
@@ -155,8 +140,9 @@ struct ref_update {
 
        /*
         * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
-        * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY, and
-        * REF_UPDATE_VIA_HEAD:
+        * REF_DELETING, REF_ISPRUNING, REF_LOG_ONLY,
+        * REF_UPDATE_VIA_HEAD, REF_NEEDS_COMMIT, and
+        * REF_DELETED_LOOSE:
         */
        unsigned int flags;
 
@@ -214,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
@@ -248,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
@@ -477,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);
 
@@ -627,64 +618,13 @@ extern struct ref_storage_be refs_be_files;
 struct ref_store {
        /* The backend describing this ref_store's storage scheme: */
        const struct ref_storage_be *be;
-
-       /*
-        * The name of the submodule represented by this object, or
-        * the empty string if it represents the main repository's
-        * reference store:
-        */
-       const char *submodule;
-
-       /*
-        * Submodule reference store instances are stored in a linked
-        * list using this pointer.
-        */
-       struct ref_store *next;
 };
 
 /*
- * Fill in the generic part of refs for the specified submodule and
- * add it to our collection of reference stores.
+ * Fill in the generic part of refs and add it to our collection of
+ * reference stores.
  */
 void base_ref_store_init(struct ref_store *refs,
-                        const struct ref_storage_be *be,
-                        const char *submodule);
-
-/*
- * Create, record, and return a ref_store instance for the specified
- * submodule (or the main repository if submodule is NULL).
- *
- * For backwards compatibility, submodule=="" is treated the same as
- * submodule==NULL.
- */
-struct ref_store *ref_store_init(const char *submodule);
-
-/*
- * 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.
- *
- * For backwards compatibility, submodule=="" is treated the same as
- * submodule==NULL.
- */
-struct ref_store *lookup_ref_store(const char *submodule);
-
-/*
- * 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);
-
-/*
- * Die if refs is for a submodule (i.e., not for the main repository).
- * caller is used in any necessary error messages.
- */
-void assert_main_repository(struct ref_store *refs, const char *caller);
+                        const struct ref_storage_be *be);
 
 #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 771d079f6edca39f8fa0d28db9bf697a99deeea6..7ff61ff5f73db07af522ac57d62ed9e699aedf72 100644 (file)
@@ -1196,11 +1196,11 @@ static void handle_refs(const char *submodule, struct rev_info *revs, unsigned f
        for_each(submodule, handle_one_ref, &cb);
 }
 
-static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
+static void handle_one_reflog_commit(struct object_id *oid, void *cb_data)
 {
        struct all_refs_cb *cb = cb_data;
-       if (!is_null_sha1(sha1)) {
-               struct object *o = parse_object(sha1);
+       if (!is_null_oid(oid)) {
+               struct object *o = parse_object(oid->hash);
                if (o) {
                        o->flags |= cb->all_flags;
                        /* ??? CMDLINEFLAGS ??? */
@@ -1214,12 +1214,12 @@ static void handle_one_reflog_commit(unsigned char *sha1, void *cb_data)
        }
 }
 
-static int handle_one_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+static int handle_one_reflog_ent(struct object_id *ooid, struct object_id *noid,
                const char *email, unsigned long timestamp, int tz,
                const char *message, void *cb_data)
 {
-       handle_one_reflog_commit(osha1, cb_data);
-       handle_one_reflog_commit(nsha1, cb_data);
+       handle_one_reflog_commit(ooid, cb_data);
+       handle_one_reflog_commit(noid, cb_data);
        return 0;
 }
 
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 d77b915db699f6f58731f645d6bcc188843d9eb8..59a4ed2ed32336b41ab8f3b1d4aca30f045aa084 100644 (file)
@@ -129,8 +129,10 @@ enum scld_error safe_create_leading_directories(char *path)
                *slash = '\0';
                if (!stat(path, &st)) {
                        /* path exists */
-                       if (!S_ISDIR(st.st_mode))
+                       if (!S_ISDIR(st.st_mode)) {
+                               errno = ENOTDIR;
                                ret = SCLD_EXISTS;
+                       }
                } else if (mkdir(path, 0777)) {
                        if (errno == EEXIST &&
                            !stat(path, &st) && S_ISDIR(st.st_mode))
@@ -158,13 +160,85 @@ enum scld_error safe_create_leading_directories(char *path)
 
 enum scld_error safe_create_leading_directories_const(const char *path)
 {
+       int save_errno;
        /* path points to cache entries, so xstrdup before messing with it */
        char *buf = xstrdup(path);
        enum scld_error result = safe_create_leading_directories(buf);
+
+       save_errno = errno;
        free(buf);
+       errno = save_errno;
        return result;
 }
 
+int raceproof_create_file(const char *path, create_file_fn fn, void *cb)
+{
+       /*
+        * The number of times we will try to remove empty directories
+        * in the way of path. This is only 1 because if another
+        * process is racily creating directories that conflict with
+        * us, we don't want to fight against them.
+        */
+       int remove_directories_remaining = 1;
+
+       /*
+        * The number of times that we will try to create the
+        * directories containing path. We are willing to attempt this
+        * more than once, because another process could be trying to
+        * clean up empty directories at the same time as we are
+        * trying to create them.
+        */
+       int create_directories_remaining = 3;
+
+       /* A scratch copy of path, filled lazily if we need it: */
+       struct strbuf path_copy = STRBUF_INIT;
+
+       int ret, save_errno;
+
+       /* Sanity check: */
+       assert(*path);
+
+retry_fn:
+       ret = fn(path, cb);
+       save_errno = errno;
+       if (!ret)
+               goto out;
+
+       if (errno == EISDIR && remove_directories_remaining-- > 0) {
+               /*
+                * A directory is in the way. Maybe it is empty; try
+                * to remove it:
+                */
+               if (!path_copy.len)
+                       strbuf_addstr(&path_copy, path);
+
+               if (!remove_dir_recursively(&path_copy, REMOVE_DIR_EMPTY_ONLY))
+                       goto retry_fn;
+       } else if (errno == ENOENT && create_directories_remaining-- > 0) {
+               /*
+                * Maybe the containing directory didn't exist, or
+                * maybe it was just deleted by a process that is
+                * racing with us to clean up empty directories. Try
+                * to create it:
+                */
+               enum scld_error scld_result;
+
+               if (!path_copy.len)
+                       strbuf_addstr(&path_copy, path);
+
+               do {
+                       scld_result = safe_create_leading_directories(path_copy.buf);
+                       if (scld_result == SCLD_OK)
+                               goto retry_fn;
+               } while (scld_result == SCLD_VANISHED && create_directories_remaining-- > 0);
+       }
+
+out:
+       strbuf_release(&path_copy);
+       errno = save_errno;
+       return ret;
+}
+
 static void fill_sha1_path(struct strbuf *buf, const unsigned char *sha1)
 {
        int i;
@@ -588,7 +662,7 @@ static int freshen_file(const char *fn)
  * either does not exist on disk, or has a stale mtime and may be subject to
  * pruning).
  */
-static int check_and_freshen_file(const char *fn, int freshen)
+int check_and_freshen_file(const char *fn, int freshen)
 {
        if (access(fn, F_OK))
                return 0;
@@ -1532,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++;
@@ -2627,6 +2701,17 @@ const unsigned char *nth_packed_object_sha1(struct packed_git *p,
        }
 }
 
+const struct object_id *nth_packed_object_oid(struct object_id *oid,
+                                             struct packed_git *p,
+                                             uint32_t n)
+{
+       const unsigned char *hash = nth_packed_object_sha1(p, n);
+       if (!hash)
+               return NULL;
+       hashcpy(oid->hash, hash);
+       return oid;
+}
+
 void check_pack_index_ptr(const struct packed_git *p, const void *vptr)
 {
        const unsigned char *ptr = vptr;
@@ -2867,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)
@@ -3396,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))
@@ -3673,15 +3760,15 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
                strbuf_setlen(path, baselen);
                strbuf_addf(path, "/%s", de->d_name);
 
-               if (strlen(de->d_name) == 38)  {
-                       char hex[41];
-                       unsigned char sha1[20];
+               if (strlen(de->d_name) == GIT_SHA1_HEXSZ - 2)  {
+                       char hex[GIT_MAX_HEXSZ+1];
+                       struct object_id oid;
 
-                       snprintf(hex, sizeof(hex), "%02x%s",
-                                subdir_nr, de->d_name);
-                       if (!get_sha1_hex(hex, sha1)) {
+                       xsnprintf(hex, sizeof(hex), "%02x%s",
+                                 subdir_nr, de->d_name);
+                       if (!get_oid_hex(hex, &oid)) {
                                if (obj_cb) {
-                                       r = obj_cb(sha1, path->buf, data);
+                                       r = obj_cb(&oid, path->buf, data);
                                        if (r)
                                                break;
                                }
@@ -3787,13 +3874,13 @@ static int for_each_object_in_pack(struct packed_git *p, each_packed_object_fn c
        int r = 0;
 
        for (i = 0; i < p->num_objects; i++) {
-               const unsigned char *sha1 = nth_packed_object_sha1(p, i);
+               struct object_id oid;
 
-               if (!sha1)
+               if (!nth_packed_object_oid(&oid, p, i))
                        return error("unable to get sha1 of object %u in %s",
                                     i, p->pack_name);
 
-               r = cb(sha1, p, i, data);
+               r = cb(&oid, p, i, data);
                if (r)
                        break;
        }
@@ -3828,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;
@@ -3885,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;
@@ -3935,7 +4021,5 @@ int read_loose_object(const char *path,
 out:
        if (map)
                munmap(map, mapsize);
-       if (fd >= 0)
-               close(fd);
        return ret;
 }
index 26ceec1d799afad6c6831cba00ca4d0ffab7979f..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;
@@ -1051,7 +1053,7 @@ struct grab_nth_branch_switch_cbdata {
        struct strbuf buf;
 };
 
-static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+static int grab_nth_branch_switch(struct object_id *ooid, struct object_id *noid,
                                  const char *email, unsigned long timestamp, int tz,
                                  const char *message, void *cb_data)
 {
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 615f4cac05f7d381b3c31831b6fd9e8a72e1f982..f519e60f87578386ec9b4ea764bf530be0f64836 100644 (file)
@@ -317,3 +317,25 @@ void replace_index_entry_in_base(struct index_state *istate,
                istate->split_index->base->cache[new->index - 1] = new;
        }
 }
+
+void add_split_index(struct index_state *istate)
+{
+       if (!istate->split_index) {
+               init_split_index(istate);
+               istate->cache_changed |= SPLIT_INDEX_ORDERED;
+       }
+}
+
+void remove_split_index(struct index_state *istate)
+{
+       if (istate->split_index) {
+               /*
+                * can't discard_split_index(&the_index); because that
+                * will destroy split_index->base->cache[], which may
+                * be shared with the_index.cache[]. So yeah we're
+                * leaking a bit here.
+                */
+               istate->split_index = NULL;
+               istate->cache_changed |= SOMETHING_CHANGED;
+       }
+}
index c1324f521a87c427c531464c5e6424fe38d7d35b..df91c1bda8117fe7d0f25a0aaedce79370801ce0 100644 (file)
@@ -31,5 +31,7 @@ void merge_base_index(struct index_state *istate);
 void prepare_to_write_split_index(struct index_state *istate);
 void finish_writing_split_index(struct index_state *istate);
 void discard_split_index(struct index_state *istate);
+void add_split_index(struct index_state *istate);
+void remove_split_index(struct index_state *istate);
 
 #endif
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 bb069bc09734f473598d78493c1c08b1b4bd85e2..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)
 {
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 0a2831d846d27e694ada4bc557a7d54a8bf2d8df..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, '/');
 
@@ -1514,3 +1783,116 @@ void absorb_git_dir_into_superproject(const char *prefix,
                strbuf_release(&sb);
        }
 }
+
+const char *get_superproject_working_tree(void)
+{
+       struct child_process cp = CHILD_PROCESS_INIT;
+       struct strbuf sb = STRBUF_INIT;
+       const char *one_up = real_path_if_valid("../");
+       const char *cwd = xgetcwd();
+       const char *ret = NULL;
+       const char *subpath;
+       int code;
+       ssize_t len;
+
+       if (!is_inside_work_tree())
+               /*
+                * FIXME:
+                * We might have a superproject, but it is harder
+                * to determine.
+                */
+               return NULL;
+
+       if (!one_up)
+               return NULL;
+
+       subpath = relative_path(cwd, one_up, &sb);
+
+       prepare_submodule_repo_env(&cp.env_array);
+       argv_array_pop(&cp.env_array);
+
+       argv_array_pushl(&cp.args, "--literal-pathspecs", "-C", "..",
+                       "ls-files", "-z", "--stage", "--full-name", "--",
+                       subpath, NULL);
+       strbuf_reset(&sb);
+
+       cp.no_stdin = 1;
+       cp.no_stderr = 1;
+       cp.out = -1;
+       cp.git_cmd = 1;
+
+       if (start_command(&cp))
+               die(_("could not start ls-files in .."));
+
+       len = strbuf_read(&sb, cp.out, PATH_MAX);
+       close(cp.out);
+
+       if (starts_with(sb.buf, "160000")) {
+               int super_sub_len;
+               int cwd_len = strlen(cwd);
+               char *super_sub, *super_wt;
+
+               /*
+                * There is a superproject having this repo as a submodule.
+                * The format is <mode> SP <hash> SP <stage> TAB <full name> \0,
+                * We're only interested in the name after the tab.
+                */
+               super_sub = strchr(sb.buf, '\t') + 1;
+               super_sub_len = sb.buf + sb.len - super_sub - 1;
+
+               if (super_sub_len > cwd_len ||
+                   strcmp(&cwd[cwd_len - super_sub_len], super_sub))
+                       die (_("BUG: returned path string doesn't match cwd?"));
+
+               super_wt = xstrdup(cwd);
+               super_wt[cwd_len - super_sub_len] = '\0';
+
+               ret = real_path(super_wt);
+               free(super_wt);
+       }
+       strbuf_release(&sb);
+
+       code = finish_command(&cp);
+
+       if (code == 128)
+               /* '../' is not a git repository */
+               return NULL;
+       if (code == 0 && len == 0)
+               /* There is an unrelated git repository at '../' */
+               return NULL;
+       if (code)
+               die(_("ls-tree returned unexpected return code %d"), code);
+
+       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 05ab674f069282b3d5b20ca69d9a1fe8f295879b..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
@@ -93,4 +123,12 @@ extern void prepare_submodule_repo_env(struct argv_array *out);
 extern void absorb_git_dir_into_superproject(const char *prefix,
                                             const char *path,
                                             unsigned flags);
+
+/*
+ * Return the absolute path of the working tree of the superproject, which this
+ * project is a submodule of. If this repository is not a submodule of
+ * another repository, return NULL.
+ */
+extern const char *get_superproject_working_tree(void);
+
 #endif
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 )
 '
index f0fbb425545019a6275a347a5826f6ba2977d394..f19ae4f8ccddacee84e04fa50663672f5f6174c1 100755 (executable)
@@ -13,10 +13,31 @@ attr_check () {
        test_line_count = 0 err
 }
 
+attr_check_quote () {
+
+       path="$1"
+       quoted_path="$2"
+       expect="$3"
+
+       git check-attr test -- "$path" >actual &&
+       echo "\"$quoted_path\": test: $expect" >expect &&
+       test_cmp expect actual
+
+}
+
+test_expect_success 'open-quoted pathname' '
+       echo "\"a test=a" >.gitattributes &&
+       test_must_fail attr_check a a
+'
+
+
 test_expect_success 'setup' '
        mkdir -p a/b/d a/c b &&
        (
                echo "[attr]notest !test"
+               echo "\" d \"   test=d"
+               echo " e        test=e"
+               echo " e\"      test=e"
                echo "f test=f"
                echo "a/i test=a/i"
                echo "onoff test -test"
@@ -69,6 +90,11 @@ test_expect_success 'command line checks' '
 '
 
 test_expect_success 'attribute test' '
+
+       attr_check " d " d &&
+       attr_check e e &&
+       attr_check_quote e\" e\\\" e &&
+
        attr_check f f &&
        attr_check a/f f &&
        attr_check a/c/f f &&
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 ea371020fa6670cd275da67ea06ed6ca09f869e9..afcca0d52c377cfabd6463f060d1b183937ba9de 100755 (executable)
@@ -1239,6 +1239,111 @@ test_expect_success 'urlmatch' '
        test_cmp expect actual
 '
 
+test_expect_success 'urlmatch favors more specific URLs' '
+       cat >.git/config <<-\EOF &&
+       [http "https://example.com/"]
+               cookieFile = /tmp/root.txt
+       [http "https://example.com/subdirectory"]
+               cookieFile = /tmp/subdirectory.txt
+       [http "https://user@example.com/"]
+               cookieFile = /tmp/user.txt
+       [http "https://averylonguser@example.com/"]
+               cookieFile = /tmp/averylonguser.txt
+       [http "https://preceding.example.com"]
+               cookieFile = /tmp/preceding.txt
+       [http "https://*.example.com"]
+               cookieFile = /tmp/wildcard.txt
+       [http "https://*.example.com/wildcardwithsubdomain"]
+               cookieFile = /tmp/wildcardwithsubdomain.txt
+       [http "https://trailing.example.com"]
+               cookieFile = /tmp/trailing.txt
+       [http "https://user@*.example.com/"]
+               cookieFile = /tmp/wildcardwithuser.txt
+       [http "https://sub.example.com/"]
+               cookieFile = /tmp/sub.txt
+       EOF
+
+       echo http.cookiefile /tmp/root.txt >expect &&
+       git config --get-urlmatch HTTP https://example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/subdirectory.txt >expect &&
+       git config --get-urlmatch HTTP https://example.com/subdirectory >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/subdirectory.txt >expect &&
+       git config --get-urlmatch HTTP https://example.com/subdirectory/nested >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/user.txt >expect &&
+       git config --get-urlmatch HTTP https://user@example.com/ >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/subdirectory.txt >expect &&
+       git config --get-urlmatch HTTP https://averylonguser@example.com/subdirectory >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/preceding.txt >expect &&
+       git config --get-urlmatch HTTP https://preceding.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/wildcard.txt >expect &&
+       git config --get-urlmatch HTTP https://wildcard.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/sub.txt >expect &&
+       git config --get-urlmatch HTTP https://sub.example.com/wildcardwithsubdomain >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/trailing.txt >expect &&
+       git config --get-urlmatch HTTP https://trailing.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.cookiefile /tmp/sub.txt >expect &&
+       git config --get-urlmatch HTTP https://user@sub.example.com >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'urlmatch with wildcard' '
+       cat >.git/config <<-\EOF &&
+       [http]
+               sslVerify
+       [http "https://*.example.com"]
+               sslVerify = false
+               cookieFile = /tmp/cookie.txt
+       EOF
+
+       test_expect_code 1 git config --bool --get-urlmatch doesnt.exist https://good.example.com >actual &&
+       test_must_be_empty actual &&
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.SSLverify https://example.com >actual &&
+       test_cmp expect actual &&
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.SSLverify https://good-example.com >actual &&
+       test_cmp expect actual &&
+
+       echo true >expect &&
+       git config --bool --get-urlmatch http.sslverify https://deep.nested.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo false >expect &&
+       git config --bool --get-urlmatch http.sslverify https://good.example.com >actual &&
+       test_cmp expect actual &&
+
+       {
+               echo http.cookiefile /tmp/cookie.txt &&
+               echo http.sslverify false
+       } >expect &&
+       git config --get-urlmatch HTTP https://good.example.com >actual &&
+       test_cmp expect actual &&
+
+       echo http.sslverify >expect &&
+       git config --get-urlmatch HTTP https://more.example.com.au >actual &&
+       test_cmp expect actual
+'
+
 # good section hygiene
 test_expect_failure 'unsetting the last key in a section removes header' '
        cat >.git/config <<-\EOF &&
index 9ba2ba11c3cac88154dbb2d522b43adf98a8dee7..933915ec06ec725f7cf6a9ed3e251f6ff2390f76 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,117 @@ 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 'conditional include, early config reading' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:foo/\"]path=bar6" >>.git/config &&
+               echo "[test]six=6" >.git/bar6 &&
+               echo 6 >expect &&
+               test-config read_early_config test.six >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..1af8c45
--- /dev/null
@@ -0,0 +1,92 @@
+#!/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)"
+'
+
+cmdline_config="'test.source=cmdline'"
+test_expect_success 'read config file in right order' '
+       echo "[test]source = home" >>.gitconfig &&
+       git init foo &&
+       (
+               cd foo &&
+               echo "[test]source = repo" >>.git/config &&
+               GIT_CONFIG_PARAMETERS=$cmdline_config test-config \
+                       read_early_config test.source >actual &&
+               cat >expected <<-\EOF &&
+               home
+               repo
+               cmdline
+               EOF
+               test_cmp expected actual
+       )
+'
+
+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 b0ffc0b5731dd746ed170604784287d3e48e9c30..dc98b4bc6dc7aab41eca38d9e1086ad2b6701fd3 100755 (executable)
@@ -35,55 +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
+'
+
+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_path_is_missing .git/$m &&
+       grep "delete-$m$" .git/logs/HEAD
+'
+
+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_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" &&
@@ -158,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' '
@@ -220,82 +239,110 @@ 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 "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_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_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' '
+       git branch e1/e2/r1 HEAD &&
+       git branch e1/r2 HEAD &&
+       git checkout e1/e2/r1 &&
+       test_when_finished "git checkout master" &&
+       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_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
 $Z $A $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000      Initial Creation
 $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
@@ -309,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
@@ -396,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 038e24c401451d61e9c93fb5c233d132a3d51acd..03d3c7f6d65b46bf977adf76a6d68582206768c2 100755 (executable)
@@ -3,7 +3,7 @@
 test_description='test git rev-parse'
 . ./test-lib.sh
 
-# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir
+# usage: [options] label is-bare is-inside-git is-inside-work prefix git-dir absolute-git-dir
 test_rev_parse () {
        d=
        bare=
@@ -29,7 +29,8 @@ test_rev_parse () {
                 --is-inside-git-dir \
                 --is-inside-work-tree \
                 --show-prefix \
-                --git-dir
+                --git-dir \
+                --absolute-git-dir
        do
                test $# -eq 0 && break
                expect="$1"
@@ -62,29 +63,71 @@ test_expect_success 'setup' '
        cp -R .git repo.git
 '
 
-test_rev_parse toplevel false false true '' .git
+test_rev_parse toplevel false false true '' .git "$ROOT/.git"
 
-test_rev_parse -C .git .git/ false true false '' .
-test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git"
+test_rev_parse -C .git .git/ false true false '' . "$ROOT/.git"
+test_rev_parse -C .git/objects .git/objects/ false true false '' "$ROOT/.git" "$ROOT/.git"
 
-test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git"
+test_rev_parse -C sub/dir subdirectory false false true sub/dir/ "$ROOT/.git" "$ROOT/.git"
 
 test_rev_parse -b t 'core.bare = true' true false false
 
 test_rev_parse -b u 'core.bare undefined' false false true
 
 
-test_rev_parse -C work -g ../.git -b f 'GIT_DIR=../.git, core.bare = false' false false true ''
+test_rev_parse -C work -g ../.git -b f 'GIT_DIR=../.git, core.bare = false' false false true '' "../.git" "$ROOT/.git"
 
 test_rev_parse -C work -g ../.git -b t 'GIT_DIR=../.git, core.bare = true' true false false ''
 
 test_rev_parse -C work -g ../.git -b u 'GIT_DIR=../.git, core.bare undefined' false false true ''
 
 
-test_rev_parse -C work -g ../repo.git -b f 'GIT_DIR=../repo.git, core.bare = false' false false true ''
+test_rev_parse -C work -g ../repo.git -b f 'GIT_DIR=../repo.git, core.bare = false' false false true '' "../repo.git" "$ROOT/repo.git"
 
 test_rev_parse -C work -g ../repo.git -b t 'GIT_DIR=../repo.git, core.bare = true' true false false ''
 
 test_rev_parse -C work -g ../repo.git -b u 'GIT_DIR=../repo.git, core.bare undefined' false false true ''
 
+test_expect_success 'git-common-dir from worktree root' '
+       echo .git >expect &&
+       git rev-parse --git-common-dir >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git-common-dir inside sub-dir' '
+       mkdir -p path/to/child &&
+       test_when_finished "rm -rf path" &&
+       echo "$(git -C path/to/child rev-parse --show-cdup).git" >expect &&
+       git -C path/to/child rev-parse --git-common-dir >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git-path from worktree root' '
+       echo .git/objects >expect &&
+       git rev-parse --git-path objects >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'git-path inside sub-dir' '
+       mkdir -p path/to/child &&
+       test_when_finished "rm -rf path" &&
+       echo "$(git -C path/to/child rev-parse --show-cdup).git/objects" >expect &&
+       git -C path/to/child rev-parse --git-path objects >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'showing the superproject correctly' '
+       git rev-parse --show-superproject-working-tree >out &&
+       test_must_be_empty out &&
+
+       test_create_repo super &&
+       test_commit -C super test_commit &&
+       test_create_repo sub &&
+       test_commit -C sub test_commit &&
+       git -C super submodule add ../sub dir/sub &&
+       echo $(pwd)/super >expect  &&
+       git -C super/dir/sub rev-parse --show-superproject-working-tree >out &&
+       test_cmp expect out
+'
+
 test_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 292a0720fccb0becca47fad2945634cbffeb89d6..af3ec0da5ac0f5e0f6e19616d9c55b611ba5a701 100755 (executable)
@@ -8,6 +8,7 @@ test_description='split index mode tests'
 sane_unset GIT_TEST_SPLIT_INDEX
 
 test_expect_success 'enable split index' '
+       git config splitIndex.maxPercentChange 100 &&
        git update-index --split-index &&
        test-dump-split-index .git/index >actual &&
        indexversion=$(test-index-version <.git/index) &&
@@ -19,12 +20,12 @@ test_expect_success 'enable split index' '
                own=8299b0bcd1ac364e5f1d7768efb62fa2da79a339
                base=39d890139ee5356c7ef572216cebcd27aa41f9df
        fi &&
-       cat >expect <<EOF &&
-own $own
-base $base
-replacements:
-deletions:
-EOF
+       cat >expect <<-EOF &&
+       own $own
+       base $base
+       replacements:
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
@@ -32,51 +33,51 @@ test_expect_success 'add one file' '
        : >one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-base $base
-100644 $EMPTY_BLOB 0   one
-replacements:
-deletions:
-EOF
+       cat >expect <<-EOF &&
+       base $base
+       100644 $EMPTY_BLOB 0    one
+       replacements:
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'disable split index' '
        git update-index --no-split-index &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        BASE=$(test-dump-split-index .git/index | grep "^own" | sed "s/own/base/") &&
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-not a split index
-EOF
+       cat >expect <<-EOF &&
+       not a split index
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'enable split index again, "one" now belongs to base index"' '
        git update-index --split-index &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-$BASE
-replacements:
-deletions:
-EOF
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
@@ -84,18 +85,18 @@ test_expect_success 'modify original file, base index untouched' '
        echo modified >one &&
        git update-index one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0       one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       q_to_tab >expect <<EOF &&
-$BASE
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
-replacements: 0
-deletions:
-EOF
+       q_to_tab >expect <<-EOF &&
+       $BASE
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+       replacements: 0
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
@@ -103,54 +104,54 @@ test_expect_success 'add another file, which stays index' '
        : >two &&
        git update-index --add two &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
-100644 $EMPTY_BLOB 0   two
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0       one
+       100644 $EMPTY_BLOB 0    two
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       q_to_tab >expect <<EOF &&
-$BASE
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
-100644 $EMPTY_BLOB 0   two
-replacements: 0
-deletions:
-EOF
+       q_to_tab >expect <<-EOF &&
+       $BASE
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+       100644 $EMPTY_BLOB 0    two
+       replacements: 0
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'remove file not in base index' '
        git update-index --force-remove two &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0      one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0       one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       q_to_tab >expect <<EOF &&
-$BASE
-100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
-replacements: 0
-deletions:
-EOF
+       q_to_tab >expect <<-EOF &&
+       $BASE
+       100644 2e0996000b7e9019eabcad29391bf0f5c7702f0b 0Q
+       replacements: 0
+       deletions:
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'remove file in base index' '
        git update-index --force-remove one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-EOF
+       cat >ls-files.expect <<-EOF &&
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-$BASE
-replacements:
-deletions: 0
-EOF
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions: 0
+       EOF
        test_cmp expect actual
 '
 
@@ -158,18 +159,18 @@ test_expect_success 'add original file back' '
        : >one &&
        git update-index --add one &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-$BASE
-100644 $EMPTY_BLOB 0   one
-replacements:
-deletions: 0
-EOF
+       cat >expect <<-EOF &&
+       $BASE
+       100644 $EMPTY_BLOB 0    one
+       replacements:
+       deletions: 0
+       EOF
        test_cmp expect actual
 '
 
@@ -177,27 +178,196 @@ test_expect_success 'add new file' '
        : >two &&
        git update-index --add two &&
        git ls-files --stage >actual &&
-       cat >expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-100644 $EMPTY_BLOB 0   two
-EOF
+       cat >expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       100644 $EMPTY_BLOB 0    two
+       EOF
        test_cmp expect actual
 '
 
 test_expect_success 'unify index, two files remain' '
        git update-index --no-split-index &&
        git ls-files --stage >ls-files.actual &&
-       cat >ls-files.expect <<EOF &&
-100644 $EMPTY_BLOB 0   one
-100644 $EMPTY_BLOB 0   two
-EOF
+       cat >ls-files.expect <<-EOF &&
+       100644 $EMPTY_BLOB 0    one
+       100644 $EMPTY_BLOB 0    two
+       EOF
        test_cmp ls-files.expect ls-files.actual &&
 
        test-dump-split-index .git/index | sed "/^own/d" >actual &&
-       cat >expect <<EOF &&
-not a split index
-EOF
+       cat >expect <<-EOF &&
+       not a split index
+       EOF
        test_cmp expect actual
 '
 
+test_expect_success 'rev-parse --shared-index-path' '
+       test_create_repo split-index &&
+       (
+               cd split-index &&
+               git update-index --split-index &&
+               echo .git/sharedindex* >expect &&
+               git rev-parse --shared-index-path >actual &&
+               test_cmp expect actual &&
+               mkdir subdirectory &&
+               cd subdirectory &&
+               echo ../.git/sharedindex* >expect &&
+               git rev-parse --shared-index-path >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'set core.splitIndex config variable to true' '
+       git config core.splitIndex true &&
+       : >three &&
+       git update-index --add three &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<-EOF &&
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       one
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       three
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       two
+       EOF
+       test_cmp ls-files.expect ls-files.actual &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'set core.splitIndex config variable to false' '
+       git config core.splitIndex false &&
+       git update-index --force-remove three &&
+       git ls-files --stage >ls-files.actual &&
+       cat >ls-files.expect <<-EOF &&
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       one
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       two
+       EOF
+       test_cmp ls-files.expect ls-files.actual &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       not a split index
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'set core.splitIndex config variable to true' '
+       git config core.splitIndex true &&
+       : >three &&
+       git update-index --add three &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual &&
+       : >four &&
+       git update-index --add four &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       four
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'check behavior with splitIndex.maxPercentChange unset' '
+       git config --unset splitIndex.maxPercentChange &&
+       : >five &&
+       git update-index --add five &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual &&
+       : >six &&
+       git update-index --add six &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       100644 e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 0       six
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'check splitIndex.maxPercentChange set to 0' '
+       git config splitIndex.maxPercentChange 0 &&
+       : >seven &&
+       git update-index --add seven &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual &&
+       : >eight &&
+       git update-index --add eight &&
+       BASE=$(test-dump-split-index .git/index | grep "^base") &&
+       test-dump-split-index .git/index | sed "/^own/d" >actual &&
+       cat >expect <<-EOF &&
+       $BASE
+       replacements:
+       deletions:
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'shared index files expire after 2 weeks by default' '
+       : >ten &&
+       git update-index --add ten &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       just_under_2_weeks_ago=$((5-14*86400)) &&
+       test-chmtime =$just_under_2_weeks_ago .git/sharedindex.* &&
+       : >eleven &&
+       git update-index --add eleven &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       just_over_2_weeks_ago=$((-1-14*86400)) &&
+       test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
+       : >twelve &&
+       git update-index --add twelve &&
+       test $(ls .git/sharedindex.* | wc -l) -le 2
+'
+
+test_expect_success 'check splitIndex.sharedIndexExpire set to 16 days' '
+       git config splitIndex.sharedIndexExpire "16.days.ago" &&
+       test-chmtime =$just_over_2_weeks_ago .git/sharedindex.* &&
+       : >thirteen &&
+       git update-index --add thirteen &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       just_over_16_days_ago=$((-1-16*86400)) &&
+       test-chmtime =$just_over_16_days_ago .git/sharedindex.* &&
+       : >fourteen &&
+       git update-index --add fourteen &&
+       test $(ls .git/sharedindex.* | wc -l) -le 2
+'
+
+test_expect_success 'check splitIndex.sharedIndexExpire set to "never" and "now"' '
+       git config splitIndex.sharedIndexExpire never &&
+       just_10_years_ago=$((-365*10*86400)) &&
+       test-chmtime =$just_10_years_ago .git/sharedindex.* &&
+       : >fifteen &&
+       git update-index --add fifteen &&
+       test $(ls .git/sharedindex.* | wc -l) -gt 2 &&
+       git config splitIndex.sharedIndexExpire now &&
+       just_1_second_ago=-1 &&
+       test-chmtime =$just_1_second_ago .git/sharedindex.* &&
+       : >sixteen &&
+       git update-index --add sixteen &&
+       test $(ls .git/sharedindex.* | wc -l) -le 2
+'
+
 test_done
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 465eeeacd3de1971c7256c84583377b3b47d520b..720063bf0d09bb8c577b78e099187abfc7da71b0 100755 (executable)
@@ -14,25 +14,35 @@ test_expect_success 'rev-parse --git-common-dir on main worktree' '
        test_cmp expected actual &&
        mkdir sub &&
        git -C sub rev-parse --git-common-dir >actual2 &&
-       echo sub/.git >expected2 &&
+       echo ../.git >expected2 &&
        test_cmp expected2 actual2
 '
 
+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 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
+'
+
 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
 '
 
@@ -41,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 &&
@@ -61,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 &&
@@ -84,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
 '
 
@@ -110,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
        )
 '
@@ -126,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 8a833f354e331966f5f927ae94ac8686dd6d166f..fe62e7c775da6a8fc191ed1dc6634d5285ddbc4b 100755 (executable)
@@ -139,6 +139,12 @@ test_expect_success 'git branch -M baz bam should succeed when baz is checked ou
        test $(git rev-parse --abbrev-ref HEAD) = bam
 '
 
+test_expect_success 'git branch -M baz bam should add entries to .git/logs/HEAD' '
+       msg="Branch: renamed refs/heads/baz to refs/heads/bam" &&
+       grep " 0\{40\}.*$msg$" .git/logs/HEAD &&
+       grep "^0\{40\}.*$msg$" .git/logs/HEAD
+'
+
 test_expect_success 'git branch -M baz bam should succeed when baz is checked out as linked working tree' '
        git checkout master &&
        git worktree add -b baz bazdir &&
@@ -207,6 +213,31 @@ test_expect_success 'git branch --list -d t should fail' '
        test_path_is_missing .git/refs/heads/t
 '
 
+test_expect_success 'git branch --list -v with --abbrev' '
+       test_when_finished "git branch -D t" &&
+       git branch t &&
+       git branch -v --list t >actual.default &&
+       git branch -v --list --abbrev t >actual.abbrev &&
+       test_cmp actual.default actual.abbrev &&
+
+       git branch -v --list --no-abbrev t >actual.noabbrev &&
+       git branch -v --list --abbrev=0 t >actual.0abbrev &&
+       test_cmp actual.noabbrev actual.0abbrev &&
+
+       git branch -v --list --abbrev=36 t >actual.36abbrev &&
+       # how many hexdigits are used?
+       read name objdefault rest <actual.abbrev &&
+       read name obj36 rest <actual.36abbrev &&
+       objfull=$(git rev-parse --verify t) &&
+
+       # are we really getting abbreviations?
+       test "$obj36" != "$objdefault" &&
+       expr "$obj36" : "$objdefault" >/dev/null &&
+       test "$objfull" != "$obj36" &&
+       expr "$objfull" : "$obj36" >/dev/null
+
+'
+
 test_expect_success 'git branch --column' '
        COLUMNS=81 git branch --column=column >actual &&
        cat >expected <<\EOF &&
@@ -947,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
index 52283dfc8cdafabba485fa6896279d600230abf9..5778c0afe12be2495b58a5251fb9bc8dc395a143 100755 (executable)
@@ -194,7 +194,7 @@ test_expect_success 'local-branch symrefs shortened properly' '
        git symbolic-ref refs/heads/ref-to-remote refs/remotes/origin/branch-one &&
        cat >expect <<-\EOF &&
          ref-to-branch -> branch-one
-         ref-to-remote -> refs/remotes/origin/branch-one
+         ref-to-remote -> origin/branch-one
        EOF
        git branch >actual.raw &&
        grep ref-to <actual.raw >actual &&
@@ -225,4 +225,18 @@ test_expect_success 'sort branches, ignore case' '
        )
 '
 
+test_expect_success 'git branch --format option' '
+       cat >expect <<-\EOF &&
+       Refname is (HEAD detached from fromtag)
+       Refname is refs/heads/ambiguous
+       Refname is refs/heads/branch-one
+       Refname is refs/heads/branch-two
+       Refname is refs/heads/master
+       Refname is refs/heads/ref-to-branch
+       Refname is refs/heads/ref-to-remote
+       EOF
+       git branch --format="Refname is %(refname)" >actual &&
+       test_cmp expect actual
+'
+
 test_done
index e2f18d11f66afe19bf28b8218042a06f452f0aa1..33d392ba112e2c130a02d2229967842ca257e837 100755 (executable)
@@ -556,7 +556,7 @@ test_expect_success 'clean error after failed "exec"' '
        echo "edited again" > file7 &&
        git add file7 &&
        test_must_fail git rebase --continue 2>error &&
-       test_i18ngrep "You have staged changes in your working tree." error
+       test_i18ngrep "you have staged changes in your working tree" error
 '
 
 test_expect_success 'rebase a detached HEAD' '
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 3c63455729c9bad8aae1210172225e5fb1aab229..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,7 +659,7 @@ 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 &&
index aaa258daa376ab6d18d71a04a517840e1b55d094..2ecb43a61652032862d5a264ede8af473ef345e1 100755 (executable)
@@ -412,4 +412,69 @@ test_expect_success 'patch-mode via -i prompts for files' '
        test_cmp expect actual
 '
 
+test_expect_success 'add -p handles globs' '
+       git reset --hard &&
+
+       mkdir -p subdir &&
+       echo base >one.c &&
+       echo base >subdir/two.c &&
+       git add "*.c" &&
+       git commit -m base &&
+
+       echo change >one.c &&
+       echo change >subdir/two.c &&
+       git add -p "*.c" <<-\EOF &&
+       y
+       y
+       EOF
+
+       cat >expect <<-\EOF &&
+       one.c
+       subdir/two.c
+       EOF
+       git diff --cached --name-only >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add -p handles relative paths' '
+       git reset --hard &&
+
+       echo base >relpath.c &&
+       git add "*.c" &&
+       git commit -m relpath &&
+
+       echo change >relpath.c &&
+       mkdir -p subdir &&
+       git -C subdir add -p .. 2>error <<-\EOF &&
+       y
+       EOF
+
+       test_must_be_empty error &&
+
+       cat >expect <<-\EOF &&
+       relpath.c
+       EOF
+       git diff --cached --name-only >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'add -p does not expand argument lists' '
+       git reset --hard &&
+
+       echo content >not-changed &&
+       git add not-changed &&
+       git commit -m "add not-changed file" &&
+
+       echo change >file &&
+       GIT_TRACE=$(pwd)/trace.out git add -p . <<-\EOF &&
+       y
+       EOF
+
+       # we know that "file" must be mentioned since we actually
+       # update it, but we want to be sure that our "." pathspec
+       # was not expanded into the argument list of any command.
+       # So look only for "not-changed".
+       ! grep not-changed trace.out
+'
+
 test_done
index 2de3e18ce68a94111c8dfcca858b8515ea8919c1..b71d1e659e97c0f34bc5bcfeda65b854c2d714e9 100755 (executable)
@@ -274,9 +274,7 @@ test_expect_success 'stash --invalid-option' '
        git add file2 &&
        test_must_fail git stash --invalid-option &&
        test_must_fail git stash save --invalid-option &&
-       test bar5,bar6 = $(cat file),$(cat file2) &&
-       git stash -- -message-starting-with-dash &&
-       test bar,bar2 = $(cat file),$(cat file2)
+       test bar5,bar6 = $(cat file),$(cat file2)
 '
 
 test_expect_success 'stash an added file' '
@@ -665,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
 '
 
@@ -775,4 +773,152 @@ test_expect_success 'stash is not confused by partial renames' '
        test_path_is_missing file
 '
 
+test_expect_success 'push -m shows right message' '
+       >foo &&
+       git add foo &&
+       git stash push -m "test message" &&
+       echo "stash@{0}: On master: test message" >expect &&
+       git stash list -1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'create stores correct message' '
+       >foo &&
+       git add foo &&
+       STASH_ID=$(git stash create "create test message") &&
+       echo "On master: create test message" >expect &&
+       git show --pretty=%s -s ${STASH_ID} >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'create with multiple arguments for the message' '
+       >foo &&
+       git add foo &&
+       STASH_ID=$(git stash create test untracked) &&
+       echo "On master: test untracked" >expect &&
+       git show --pretty=%s -s ${STASH_ID} >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'stash -- <pathspec> stashes and restores the file' '
+       >foo &&
+       >bar &&
+       git add foo bar &&
+       git stash push -- foo &&
+       test_path_is_file bar &&
+       test_path_is_missing foo &&
+       git stash pop &&
+       test_path_is_file foo &&
+       test_path_is_file bar
+'
+
+test_expect_success 'stash with multiple pathspec arguments' '
+       >foo &&
+       >bar &&
+       >extra &&
+       git add foo bar extra &&
+       git stash push -- foo bar &&
+       test_path_is_missing bar &&
+       test_path_is_missing foo &&
+       test_path_is_file extra &&
+       git stash pop &&
+       test_path_is_file foo &&
+       test_path_is_file bar &&
+       test_path_is_file extra
+'
+
+test_expect_success 'stash with file including $IFS character' '
+       >"foo bar" &&
+       >foo &&
+       >bar &&
+       git add foo* &&
+       git stash push -- "foo b*" &&
+       test_path_is_missing "foo bar" &&
+       test_path_is_file foo &&
+       test_path_is_file bar &&
+       git stash pop &&
+       test_path_is_file "foo bar" &&
+       test_path_is_file foo &&
+       test_path_is_file bar
+'
+
+test_expect_success 'stash with pathspec matching multiple paths' '
+       echo original >file &&
+       echo original >other-file &&
+       git commit -m "two" file other-file &&
+       echo modified >file &&
+       echo modified >other-file &&
+       git stash push -- "*file" &&
+       echo original >expect &&
+       test_cmp expect file &&
+       test_cmp expect other-file &&
+       git stash pop &&
+       echo modified >expect &&
+       test_cmp expect file &&
+       test_cmp expect other-file
+'
+
+test_expect_success 'stash push -p with pathspec shows no changes only once' '
+       >foo &&
+       git add foo &&
+       git commit -m "tmp" &&
+       git stash push -p foo >actual &&
+       echo "No local changes to save" >expect &&
+       git reset --hard HEAD~ &&
+       test_cmp expect actual
+'
+
+test_expect_success 'stash push with pathspec shows no changes when there are none' '
+       >foo &&
+       git add foo &&
+       git commit -m "tmp" &&
+       git stash push foo >actual &&
+       echo "No local changes to save" >expect &&
+       git reset --hard HEAD~ &&
+       test_cmp expect actual
+'
+
+test_expect_success 'stash push with pathspec not in the repository errors out' '
+       >untracked &&
+       test_must_fail git stash push untracked &&
+       test_path_is_file untracked
+'
+
+test_expect_success 'untracked files are left in place when -u is not given' '
+       >file &&
+       git add file &&
+       >untracked &&
+       git stash push file &&
+       test_path_is_file untracked
+'
+
+test_expect_success 'stash without verb with pathspec' '
+       >"foo bar" &&
+       >foo &&
+       >bar &&
+       git add foo* &&
+       git stash -- "foo b*" &&
+       test_path_is_missing "foo bar" &&
+       test_path_is_file foo &&
+       test_path_is_file bar &&
+       git stash pop &&
+       test_path_is_file "foo bar" &&
+       test_path_is_file foo &&
+       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 f372fc8ca8e3b311b03313a46db1aa8f35de2ea9..193adc7b68d691526b804fa7ce02e1f42f21b8a4 100755 (executable)
@@ -185,4 +185,30 @@ test_expect_success 'stash save --all is stash poppable' '
        test -s .gitignore
 '
 
+test_expect_success 'stash push --include-untracked with pathspec' '
+       >foo &&
+       >bar &&
+       git stash push --include-untracked -- foo &&
+       test_path_is_file bar &&
+       test_path_is_missing foo &&
+       git stash pop &&
+       test_path_is_file bar &&
+       test_path_is_file foo
+'
+
+test_expect_success 'stash push with $IFS character' '
+       >"foo bar" &&
+       >foo &&
+       >bar &&
+       git add foo* &&
+       git stash push --include-untracked -- "foo b*" &&
+       test_path_is_missing "foo bar" &&
+       test_path_is_file foo &&
+       test_path_is_file bar &&
+       git stash pop &&
+       test_path_is_file "foo bar" &&
+       test_path_is_file foo &&
+       test_path_is_file bar
+'
+
 test_done
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 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 305ca7a9308685c7d040ac254fd34cebbbc86cc8..3331e0f53443f9a8566d01ac08dd0cbc5aa2d33b 100755 (executable)
@@ -255,4 +255,42 @@ test_expect_success 'deny pushing to delete current branch' '
        )
 '
 
+extract_ref_advertisement () {
+       perl -lne '
+               # \\ is there to skip capabilities after \0
+               /push< ([^\\]+)/ or next;
+               exit 0 if $1 eq "0000";
+               print $1;
+       '
+}
+
+test_expect_success 'receive-pack de-dupes .have lines' '
+       git init shared &&
+       git -C shared commit --allow-empty -m both &&
+       git clone -s shared fork &&
+       (
+               cd shared &&
+               git checkout -b only-shared &&
+               git commit --allow-empty -m only-shared &&
+               git update-ref refs/heads/foo HEAD
+       ) &&
+
+       # Notable things in this expectation:
+       #  - local refs are not de-duped
+       #  - .have does not duplicate locals
+       #  - .have does not duplicate itself
+       local=$(git -C fork rev-parse HEAD) &&
+       shared=$(git -C shared rev-parse only-shared) &&
+       cat >expect <<-EOF &&
+       $local refs/heads/master
+       $local refs/remotes/origin/HEAD
+       $local refs/remotes/origin/master
+       $shared .have
+       EOF
+
+       GIT_TRACE_PACKET=$(pwd)/trace git push fork HEAD:foo &&
+       extract_ref_advertisement <trace >refs &&
+       test_cmp expect refs
+'
+
 test_done
index 3ea27107c2ddee8217d5d91c4b13d85d81b44a5e..a6c0178f3af6c3e3f01816197e51e9bca546b00a 100755 (executable)
@@ -744,7 +744,7 @@ test_expect_success 'rename a remote' '
        (
                cd four &&
                git remote rename origin upstream &&
-               rmdir .git/refs/remotes/origin &&
+               test -z "$(git for-each-ref refs/remotes/origin)" &&
                test "$(git symbolic-ref refs/remotes/upstream/HEAD)" = "refs/remotes/upstream/master" &&
                test "$(git rev-parse upstream/master)" = "$(git rev-parse master)" &&
                test "$(git config remote.upstream.fetch)" = "+refs/heads/*:refs/remotes/upstream/*" &&
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 a2c9e7439f362d8f247c369ba515a55b26dee8ec..d38ecee2172a6e5599cfbeb18e472816e98e553c 100755 (executable)
@@ -229,4 +229,33 @@ test_expect_success 'new branch already exists' '
        )
 '
 
+test_expect_success 'background updates of REMOTE can be mitigated with a non-updated REMOTE-push' '
+       rm -rf src dst &&
+       git init --bare src.bare &&
+       test_when_finished "rm -rf src.bare" &&
+       git clone --no-local src.bare dst &&
+       test_when_finished "rm -rf dst" &&
+       (
+               cd dst &&
+               test_commit G &&
+               git remote add origin-push ../src.bare &&
+               git push origin-push master:master
+       ) &&
+       git clone --no-local src.bare dst2 &&
+       test_when_finished "rm -rf dst2" &&
+       (
+               cd dst2 &&
+               test_commit H &&
+               git push
+       ) &&
+       (
+               cd dst &&
+               test_commit I &&
+               git fetch origin &&
+               test_must_fail git push --force-with-lease origin-push &&
+               git fetch origin-push &&
+               git push --force-with-lease origin-push
+       )
+'
+
 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 4241ea5b32db4a0a85810dfe6421a161ce55fc46..9c56f771b619e4bb931ce3d34a5f9325b8f61e90 100755 (executable)
@@ -386,6 +386,53 @@ test_expect_success 'tortoiseplink is like putty, with extra arguments' '
        expect_ssh "-batch -P 123" myhost src
 '
 
+test_expect_success 'double quoted plink.exe in GIT_SSH_COMMAND' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
+       GIT_SSH_COMMAND="\"$TRASH_DIRECTORY/plink.exe\" -v" \
+               git clone "[myhost:123]:src" ssh-bracket-clone-plink-3 &&
+       expect_ssh "-v -P 123" myhost src
+'
+
+SQ="'"
+test_expect_success 'single quoted plink.exe in GIT_SSH_COMMAND' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink.exe" &&
+       GIT_SSH_COMMAND="$SQ$TRASH_DIRECTORY/plink.exe$SQ -v" \
+               git clone "[myhost:123]:src" ssh-bracket-clone-plink-4 &&
+       expect_ssh "-v -P 123" myhost src
+'
+
+test_expect_success 'GIT_SSH_VARIANT overrides plink detection' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
+       GIT_SSH_VARIANT=ssh \
+       git clone "[myhost:123]:src" ssh-bracket-clone-variant-1 &&
+       expect_ssh "-p 123" myhost src
+'
+
+test_expect_success 'ssh.variant overrides plink detection' '
+       copy_ssh_wrapper_as "$TRASH_DIRECTORY/plink" &&
+       git -c ssh.variant=ssh \
+               clone "[myhost:123]:src" ssh-bracket-clone-variant-2 &&
+       expect_ssh "-p 123" myhost src
+'
+
+test_expect_success 'GIT_SSH_VARIANT overrides plink detection to plink' '
+       GIT_SSH_VARIANT=plink \
+       git clone "[myhost:123]:src" ssh-bracket-clone-variant-3 &&
+       expect_ssh "-P 123" myhost src
+'
+
+test_expect_success 'GIT_SSH_VARIANT overrides plink to tortoiseplink' '
+       GIT_SSH_VARIANT=tortoiseplink \
+       git clone "[myhost:123]:src" ssh-bracket-clone-variant-4 &&
+       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 1408b608eb030f0e6bcc3d531aad426da5f06b12..29597451967dd7e4847a5114b79a91399e1b3ab3 100755 (executable)
@@ -99,6 +99,44 @@ test_expect_success '--cherry-pick bar does not come up empty (II)' '
        test_cmp actual.named expect
 '
 
+test_expect_success 'name-rev multiple --refs combine inclusive' '
+       git rev-list --left-right --cherry-pick F...E -- bar >actual &&
+       git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \
+               <actual >actual.named &&
+       test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+<tags/F
+EOF
+
+test_expect_success 'name-rev --refs excludes non-matched patterns' '
+       git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&
+       git rev-list --left-right --cherry-pick F...E -- bar >actual &&
+       git name-rev --stdin --name-only --refs="*tags/F" \
+               <actual >actual.named &&
+       test_cmp actual.named expect
+'
+
+cat >expect <<EOF
+<tags/F
+EOF
+
+test_expect_success 'name-rev --exclude excludes matched patterns' '
+       git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&
+       git rev-list --left-right --cherry-pick F...E -- bar >actual &&
+       git name-rev --stdin --name-only --refs="*tags/*" --exclude="*E" \
+               <actual >actual.named &&
+       test_cmp actual.named expect
+'
+
+test_expect_success 'name-rev --no-refs clears the refs list' '
+       git rev-list --left-right --cherry-pick F...E -- bar >expect &&
+       git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \
+               <expect >actual &&
+       test_cmp actual expect
+'
+
 cat >expect <<EOF
 +tags/F
 =tags/D
index 3d5c238c8150c13b1244f8c4208856714265245a..97a07655a0e0d74747259498bfbb8d03ea6c7480 100755 (executable)
@@ -44,7 +44,7 @@ b1 [ahead 1, behind 1] d
 b2 [ahead 1, behind 1] d
 b3 [behind 1] b
 b4 [ahead 2] f
-b5 g
+b5 [gone] g
 b6 c
 EOF
 
diff --git a/t/t6045-merge-rename-delete.sh b/t/t6045-merge-rename-delete.sh
new file mode 100755 (executable)
index 0000000..5d33577
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+test_description='Merge-recursive rename/delete conflict message'
+. ./test-lib.sh
+
+test_expect_success 'rename/delete' '
+       echo foo >A &&
+       git add A &&
+       git commit -m "initial" &&
+
+       git checkout -b rename &&
+       git mv A B &&
+       git commit -m "rename" &&
+
+       git checkout master &&
+       git rm A &&
+       git commit -m "delete" &&
+
+       test_must_fail git merge --strategy=recursive rename >output &&
+       test_i18ngrep "CONFLICT (rename/delete): A deleted in HEAD and renamed to B in rename. Version rename of B left in tree." output
+'
+
+test_done
index 85f269411cb3aed461f6894dc5d1cc780debc24b..16952e44fcfd705e115b81099bdbff809ff1520d 100755 (executable)
@@ -182,6 +182,10 @@ check_describe "test2-lightweight-*" --tags --match="test2-*"
 
 check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
 
+check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="test2-*" HEAD^
+
+check_describe "test2-lightweight-*" --long --tags --match="test1-*" --no-match --match="test2-*" HEAD^
+
 test_expect_success 'name-rev with exact tags' '
        echo A >expect &&
        tag_object=$(git rev-parse refs/tags/A) &&
@@ -206,4 +210,47 @@ test_expect_success 'describe --contains with the exact tags' '
        test_cmp expect actual
 '
 
+test_expect_success 'describe --contains and --match' '
+       echo "A^0" >expect &&
+       tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+       test_must_fail git describe --contains --match="B" $tagged_commit &&
+       git describe --contains --match="B" --match="A" $tagged_commit >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'describe --exclude' '
+       echo "c~1" >expect &&
+       tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+       test_must_fail git describe --contains --match="B" $tagged_commit &&
+       git describe --contains --match="?" --exclude="A" $tagged_commit >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'describe --contains and --no-match' '
+       echo "A^0" >expect &&
+       tagged_commit=$(git rev-parse "refs/tags/A^0") &&
+       git describe --contains --match="B" --no-match $tagged_commit >actual &&
+       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 d51595cf6b189a59d271dcc69c3f245b72dcb8f6..9dd5cde5fc53deb365234e815d7142de9a975005 100755 (executable)
@@ -25,8 +25,10 @@ EOF
        test_cmp expect actual
 '
 
-test_expect_success 'exclude only should error out' '
-       test_must_fail git log --oneline --format=%s -- ":(exclude)sub"
+test_expect_success 'exclude only no longer errors out' '
+       git log --oneline --format=%s -- . ":(exclude)sub" >expect &&
+       git log --oneline --format=%s -- ":(exclude)sub" >actual &&
+       test_cmp expect actual
 '
 
 test_expect_success 't_e_i() exclude sub' '
diff --git a/t/t6135-pathspec-with-attrs.sh b/t/t6135-pathspec-with-attrs.sh
new file mode 100755 (executable)
index 0000000..77b8cef
--- /dev/null
@@ -0,0 +1,200 @@
+#!/bin/sh
+
+test_description='test labels in pathspecs'
+. ./test-lib.sh
+
+test_expect_success 'setup a tree' '
+       cat <<-\EOF >expect &&
+       fileA
+       fileAB
+       fileAC
+       fileB
+       fileBC
+       fileC
+       fileNoLabel
+       fileSetLabel
+       fileUnsetLabel
+       fileValue
+       fileWrongLabel
+       sub/fileA
+       sub/fileAB
+       sub/fileAC
+       sub/fileB
+       sub/fileBC
+       sub/fileC
+       sub/fileNoLabel
+       sub/fileSetLabel
+       sub/fileUnsetLabel
+       sub/fileValue
+       sub/fileWrongLabel
+       EOF
+       mkdir sub &&
+       while read path
+       do
+               : >$path &&
+               git add $path || return 1
+       done <expect &&
+       git commit -m "initial commit" &&
+       git ls-files >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'pathspec with no attr' '
+       test_must_fail git ls-files ":(attr:)"
+'
+
+test_expect_success 'pathspec with labels and non existent .gitattributes' '
+       git ls-files ":(attr:label)" >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'setup .gitattributes' '
+       cat <<-\EOF >.gitattributes &&
+       fileA labelA
+       fileB labelB
+       fileC labelC
+       fileAB labelA labelB
+       fileAC labelA labelC
+       fileBC labelB labelC
+       fileUnsetLabel -label
+       fileSetLabel label
+       fileValue label=foo
+       fileWrongLabel label☺
+       EOF
+       git add .gitattributes &&
+       git commit -m "add attributes"
+'
+
+test_expect_success 'check specific set attr' '
+       cat <<-\EOF >expect &&
+       fileSetLabel
+       sub/fileSetLabel
+       EOF
+       git ls-files ":(attr:label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check specific unset attr' '
+       cat <<-\EOF >expect &&
+       fileUnsetLabel
+       sub/fileUnsetLabel
+       EOF
+       git ls-files ":(attr:-label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check specific value attr' '
+       cat <<-\EOF >expect &&
+       fileValue
+       sub/fileValue
+       EOF
+       git ls-files ":(attr:label=foo)" >actual &&
+       test_cmp expect actual &&
+       git ls-files ":(attr:label=bar)" >actual &&
+       test_must_be_empty actual
+'
+
+test_expect_success 'check unspecified attr' '
+       cat <<-\EOF >expect &&
+       .gitattributes
+       fileA
+       fileAB
+       fileAC
+       fileB
+       fileBC
+       fileC
+       fileNoLabel
+       fileWrongLabel
+       sub/fileA
+       sub/fileAB
+       sub/fileAC
+       sub/fileB
+       sub/fileBC
+       sub/fileC
+       sub/fileNoLabel
+       sub/fileWrongLabel
+       EOF
+       git ls-files ":(attr:!label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check multiple unspecified attr' '
+       cat <<-\EOF >expect &&
+       .gitattributes
+       fileC
+       fileNoLabel
+       fileWrongLabel
+       sub/fileC
+       sub/fileNoLabel
+       sub/fileWrongLabel
+       EOF
+       git ls-files ":(attr:!labelB !labelA !label)" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check label with more labels but excluded path' '
+       cat <<-\EOF >expect &&
+       fileAB
+       fileB
+       fileBC
+       EOF
+       git ls-files ":(attr:labelB)" ":(exclude)sub/" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'check label excluding other labels' '
+       cat <<-\EOF >expect &&
+       fileAB
+       fileB
+       fileBC
+       sub/fileAB
+       sub/fileB
+       EOF
+       git ls-files ":(attr:labelB)" ":(exclude,attr:labelC)sub/" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'fail on multiple attr specifiers in one pathspec item' '
+       test_must_fail git ls-files . ":(attr:labelB,attr:labelC)" 2>actual &&
+       test_i18ngrep "Only one" actual
+'
+
+test_expect_success 'fail if attr magic is used places not implemented' '
+       # The main purpose of this test is to check that we actually fail
+       # when you attempt to use attr magic in commands that do not implement
+       # attr magic. This test does not advocate git-add to stay that way,
+       # though, but git-add is convenient as it has its own internal pathspec
+       # parsing.
+       test_must_fail git add ":(attr:labelB)" 2>actual &&
+       test_i18ngrep "unsupported magic" actual
+'
+
+test_expect_success 'abort on giving invalid label on the command line' '
+       test_must_fail git ls-files . ":(attr:☺)"
+'
+
+test_expect_success 'abort on asking for wrong magic' '
+       test_must_fail git ls-files . ":(attr:-label=foo)" &&
+       test_must_fail git ls-files . ":(attr:!label=foo)"
+'
+
+test_expect_success 'check attribute list' '
+       cat <<-EOF >>.gitattributes &&
+       * whitespace=indent,trail,space
+       EOF
+       git ls-files ":(attr:whitespace=indent\,trail\,space)" >actual &&
+       git ls-files >expect &&
+       test_cmp expect actual
+'
+
+test_expect_success 'backslash cannot be the last character' '
+       test_must_fail git ls-files ":(attr:label=foo\\ labelA=bar)" 2>actual &&
+       test_i18ngrep "not allowed as last character in attr value" actual
+'
+
+test_expect_success 'backslash cannot be used as a value' '
+       test_must_fail git ls-files ":(attr:label=f\\\oo)" 2>actual &&
+       test_i18ngrep "for value matching" actual
+'
+
+test_done
index a468041c50d9ef2c093ee4f149f297ad970065cf..834a9ed168fad35258955447326e7d3d1f09b7d1 100755 (executable)
@@ -38,6 +38,7 @@ test_atom() {
        case "$1" in
                head) ref=refs/heads/master ;;
                 tag) ref=refs/tags/testtag ;;
+                sym) ref=refs/heads/sym ;;
                   *) ref=$1 ;;
        esac
        printf '%s\n' "$3" >expected
@@ -50,16 +51,40 @@ test_atom() {
 
 test_atom head refname refs/heads/master
 test_atom head refname:short master
+test_atom head refname:lstrip=1 heads/master
+test_atom head refname:lstrip=2 master
+test_atom head refname:lstrip=-1 master
+test_atom head refname:lstrip=-2 heads/master
+test_atom head refname:rstrip=1 refs/heads
+test_atom head refname:rstrip=2 refs
+test_atom head refname:rstrip=-1 refs
+test_atom head refname:rstrip=-2 refs/heads
 test_atom head refname:strip=1 heads/master
 test_atom head refname:strip=2 master
+test_atom head refname:strip=-1 master
+test_atom head refname:strip=-2 heads/master
 test_atom head upstream refs/remotes/origin/master
 test_atom head upstream:short origin/master
+test_atom head upstream:lstrip=2 origin/master
+test_atom head upstream:lstrip=-2 origin/master
+test_atom head upstream:rstrip=2 refs/remotes
+test_atom head upstream:rstrip=-2 refs/remotes
+test_atom head upstream:strip=2 origin/master
+test_atom head upstream:strip=-2 origin/master
 test_atom head push refs/remotes/myfork/master
 test_atom head push:short myfork/master
+test_atom head push:lstrip=1 remotes/myfork/master
+test_atom head push:lstrip=-1 master
+test_atom head push:rstrip=1 refs/remotes/myfork
+test_atom head push:rstrip=-1 refs
+test_atom head push:strip=1 remotes/myfork/master
+test_atom head push:strip=-1 master
 test_atom head objecttype commit
 test_atom head objectsize 171
 test_atom head objectname $(git rev-parse refs/heads/master)
 test_atom head objectname:short $(git rev-parse --short refs/heads/master)
+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)
 test_atom head tree $(git rev-parse refs/heads/master^{tree})
 test_atom head parent ''
 test_atom head numparent 0
@@ -99,6 +124,8 @@ test_atom tag objecttype tag
 test_atom tag objectsize 154
 test_atom tag objectname $(git rev-parse refs/tags/testtag)
 test_atom tag objectname:short $(git rev-parse --short refs/tags/testtag)
+test_atom head objectname:short=1 $(git rev-parse --short=1 refs/heads/master)
+test_atom head objectname:short=10 $(git rev-parse --short=10 refs/heads/master)
 test_atom tag tree ''
 test_atom tag parent ''
 test_atom tag numparent ''
@@ -134,16 +161,6 @@ test_expect_success 'Check invalid atoms names are errors' '
        test_must_fail git for-each-ref --format="%(INVALID)" refs/heads
 '
 
-test_expect_success 'arguments to :strip must be positive integers' '
-       test_must_fail git for-each-ref --format="%(refname:strip=0)" &&
-       test_must_fail git for-each-ref --format="%(refname:strip=-1)" &&
-       test_must_fail git for-each-ref --format="%(refname:strip=foo)"
-'
-
-test_expect_success 'stripping refnames too far gives an error' '
-       test_must_fail git for-each-ref --format="%(refname:strip=3)"
-'
-
 test_expect_success 'Check format specifiers are ignored in naming date atoms' '
        git for-each-ref --format="%(authordate)" refs/heads &&
        git for-each-ref --format="%(authordate:default) %(authordate)" refs/heads &&
@@ -164,6 +181,12 @@ test_expect_success 'Check invalid format specifiers are errors' '
        test_must_fail git for-each-ref --format="%(authordate:INVALID)" refs/heads
 '
 
+test_expect_success 'arguments to %(objectname:short=) must be positive integers' '
+       test_must_fail git for-each-ref --format="%(objectname:short=0)" &&
+       test_must_fail git for-each-ref --format="%(objectname:short=-1)" &&
+       test_must_fail git for-each-ref --format="%(objectname:short=foo)"
+'
+
 test_date () {
        f=$1 &&
        committer_date=$2 &&
@@ -362,6 +385,8 @@ test_expect_success 'setup for upstream:track[short]' '
 
 test_atom head upstream:track '[ahead 1]'
 test_atom head upstream:trackshort '>'
+test_atom head upstream:track,nobracket 'ahead 1'
+test_atom head upstream:nobracket,track 'ahead 1'
 test_atom head push:track '[ahead 1]'
 test_atom head push:trackshort '>'
 
@@ -372,7 +397,7 @@ test_expect_success 'Check that :track[short] cannot be used with other atoms' '
 
 test_expect_success 'Check that :track[short] works when upstream is invalid' '
        cat >expected <<-\EOF &&
-
+       [gone]
 
        EOF
        test_when_finished "git config branch.master.merge refs/heads/master" &&
@@ -554,6 +579,7 @@ test_expect_success 'Verify sort with multiple keys' '
        test_cmp expected actual
 '
 
+
 test_expect_success 'do not dereference NULL upon %(HEAD) on unborn branch' '
        test_when_finished "git checkout master" &&
        git for-each-ref --format="%(HEAD) %(refname:short)" refs/heads/ >actual &&
@@ -588,4 +614,52 @@ test_expect_success 'basic atom: head contents:trailers' '
        test_cmp expect actual.clean
 '
 
+test_expect_success 'Add symbolic ref for the following tests' '
+       git symbolic-ref refs/heads/sym refs/heads/master
+'
+
+cat >expected <<EOF
+refs/heads/master
+EOF
+
+test_expect_success 'Verify usage of %(symref) atom' '
+       git for-each-ref --format="%(symref)" refs/heads/sym >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+heads/master
+EOF
+
+test_expect_success 'Verify usage of %(symref:short) atom' '
+       git for-each-ref --format="%(symref:short)" refs/heads/sym >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+master
+heads/master
+EOF
+
+test_expect_success 'Verify usage of %(symref:lstrip) atom' '
+       git for-each-ref --format="%(symref:lstrip=2)" refs/heads/sym > actual &&
+       git for-each-ref --format="%(symref:lstrip=-2)" refs/heads/sym >> actual &&
+       test_cmp expected actual &&
+
+       git for-each-ref --format="%(symref:strip=2)" refs/heads/sym > actual &&
+       git for-each-ref --format="%(symref:strip=-2)" refs/heads/sym >> actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+refs
+refs/heads
+EOF
+
+test_expect_success 'Verify usage of %(symref:rstrip) atom' '
+       git for-each-ref --format="%(symref:rstrip=2)" refs/heads/sym > actual &&
+       git for-each-ref --format="%(symref:rstrip=-2)" refs/heads/sym >> actual &&
+       test_cmp expected actual
+'
+
 test_done
index d0ab09f4bd804730eb049ea982e5724653583f06..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)"
 '
@@ -327,4 +343,102 @@ test_expect_success 'reverse version sort' '
        test_cmp expect actual
 '
 
+test_expect_success 'improper usage of %(if), %(then), %(else) and %(end) atoms' '
+       test_must_fail git for-each-ref --format="%(if)" &&
+       test_must_fail git for-each-ref --format="%(then) %(end)" &&
+       test_must_fail git for-each-ref --format="%(else) %(end)" &&
+       test_must_fail git for-each-ref --format="%(if) %(else) %(end)" &&
+       test_must_fail git for-each-ref --format="%(if) %(then) %(then) %(end)" &&
+       test_must_fail git for-each-ref --format="%(then) %(else) %(end)" &&
+       test_must_fail git for-each-ref --format="%(if) %(else) %(end)" &&
+       test_must_fail git for-each-ref --format="%(if) %(then) %(else)" &&
+       test_must_fail git for-each-ref --format="%(if) %(else) %(then) %(end)" &&
+       test_must_fail git for-each-ref --format="%(if) %(then) %(else) %(else) %(end)" &&
+       test_must_fail git for-each-ref --format="%(if) %(end)"
+'
+
+test_expect_success 'check %(if)...%(then)...%(end) atoms' '
+       git for-each-ref --format="%(refname)%(if)%(authorname)%(then) Author: %(authorname)%(end)" >actual &&
+       cat >expect <<-\EOF &&
+       refs/heads/master Author: A U Thor
+       refs/heads/side Author: A U Thor
+       refs/odd/spot Author: A U Thor
+       refs/tags/annotated-tag
+       refs/tags/doubly-annotated-tag
+       refs/tags/doubly-signed-tag
+       refs/tags/foo1.10 Author: A U Thor
+       refs/tags/foo1.3 Author: A U Thor
+       refs/tags/foo1.6 Author: A U Thor
+       refs/tags/four Author: A U Thor
+       refs/tags/one Author: A U Thor
+       refs/tags/signed-tag
+       refs/tags/three Author: A U Thor
+       refs/tags/two Author: A U Thor
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'check %(if)...%(then)...%(else)...%(end) atoms' '
+       git for-each-ref --format="%(if)%(authorname)%(then)%(authorname)%(else)No author%(end): %(refname)" >actual &&
+       cat >expect <<-\EOF &&
+       A U Thor: refs/heads/master
+       A U Thor: refs/heads/side
+       A U Thor: refs/odd/spot
+       No author: refs/tags/annotated-tag
+       No author: refs/tags/doubly-annotated-tag
+       No author: refs/tags/doubly-signed-tag
+       A U Thor: refs/tags/foo1.10
+       A U Thor: refs/tags/foo1.3
+       A U Thor: refs/tags/foo1.6
+       A U Thor: refs/tags/four
+       A U Thor: refs/tags/one
+       No author: refs/tags/signed-tag
+       A U Thor: refs/tags/three
+       A U Thor: refs/tags/two
+       EOF
+       test_cmp expect actual
+'
+test_expect_success 'ignore spaces in %(if) atom usage' '
+       git for-each-ref --format="%(refname:short): %(if)%(HEAD)%(then)Head ref%(else)Not Head ref%(end)" >actual &&
+       cat >expect <<-\EOF &&
+       master: Head ref
+       side: Not Head ref
+       odd/spot: Not Head ref
+       annotated-tag: Not Head ref
+       doubly-annotated-tag: Not Head ref
+       doubly-signed-tag: Not Head ref
+       foo1.10: Not Head ref
+       foo1.3: Not Head ref
+       foo1.6: Not Head ref
+       four: Not Head ref
+       one: Not Head ref
+       signed-tag: Not Head ref
+       three: Not Head ref
+       two: Not Head ref
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'check %(if:equals=<string>)' '
+       git for-each-ref --format="%(if:equals=master)%(refname:short)%(then)Found master%(else)Not master%(end)" refs/heads/ >actual &&
+       cat >expect <<-\EOF &&
+       Found master
+       Not master
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'check %(if:notequals=<string>)' '
+       git for-each-ref --format="%(if:notequals=master)%(refname:short)%(then)Not master%(else)Found master%(end)" refs/heads/ >actual &&
+       cat >expect <<-\EOF &&
+       Found master
+       Not master
+       EOF
+       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 b8ad076c25680cad1f9faa1de544980c78547f6c..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
@@ -81,9 +80,25 @@ test_expect_success 'creating a tag using default HEAD should succeed' '
 '
 
 test_expect_success 'creating a tag with --create-reflog should create reflog' '
+       git log -1 \
+               --format="format:tag: tagging %h (%s, %cd)%n" \
+               --date=format:%Y-%m-%d >expected &&
        test_when_finished "git tag -d tag_with_reflog" &&
        git tag --create-reflog tag_with_reflog &&
-       git reflog exists refs/tags/tag_with_reflog
+       git reflog exists refs/tags/tag_with_reflog &&
+       sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'annotated tag with --create-reflog has correct message' '
+       git log -1 \
+               --format="format:tag: tagging %h (%s, %cd)%n" \
+               --date=format:%Y-%m-%d >expected &&
+       test_when_finished "git tag -d tag_with_reflog" &&
+       git tag -m "annotated tag" --create-reflog tag_with_reflog &&
+       git reflog exists refs/tags/tag_with_reflog &&
+       sed -e "s/^.*   //" .git/logs/refs/tags/tag_with_reflog >actual &&
+       test_cmp expected actual
 '
 
 test_expect_success '--create-reflog does not create reflog on failure' '
@@ -103,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
@@ -120,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 \
@@ -322,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 &&
@@ -604,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 &&
@@ -617,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 &&
@@ -1367,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
@@ -1376,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
@@ -1385,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' '
@@ -1406,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
@@ -1420,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
@@ -1435,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' '
@@ -1691,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
@@ -1707,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' '
@@ -1726,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' '
@@ -1747,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 &&
@@ -1755,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 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
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 a34e55f874ca06da4cae6ab5d46bc3263c58d521..5ed28135bea13a6c4082413044d8dcb205449c38 100755 (executable)
@@ -98,7 +98,7 @@ test_gitcomp ()
 {
        local -a COMPREPLY &&
        sed -e 's/Z$//' >expected &&
-       cur="$1" &&
+       local cur="$1" &&
        shift &&
        __gitcomp "$@" &&
        print_comp &&
@@ -113,7 +113,7 @@ test_gitcomp_nl ()
 {
        local -a COMPREPLY &&
        sed -e 's/Z$//' >expected &&
-       cur="$1" &&
+       local cur="$1" &&
        shift &&
        __gitcomp_nl "$@" &&
        print_comp &&
@@ -124,140 +124,296 @@ invalid_variable_name='${foo.bar}'
 
 actual="$TRASH_DIRECTORY/actual"
 
-test_expect_success 'setup for __gitdir tests' '
+if test_have_prereq MINGW
+then
+       ROOT="$(pwd -W)"
+else
+       ROOT="$(pwd)"
+fi
+
+test_expect_success 'setup for __git_find_repo_path/__gitdir tests' '
        mkdir -p subdir/subsubdir &&
+       mkdir -p non-repo &&
        git init otherrepo
 '
 
-test_expect_success '__gitdir - from command line (through $__git_dir)' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+test_expect_success '__git_find_repo_path - from command line (through $__git_dir)' '
+       echo "$ROOT/otherrepo/.git" >expected &&
        (
-               __git_dir="$TRASH_DIRECTORY/otherrepo/.git" &&
-               __gitdir >"$actual"
+               __git_dir="$ROOT/otherrepo/.git" &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - repo as argument' '
-       echo "otherrepo/.git" >expected &&
-       __gitdir "otherrepo" >"$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success '__gitdir - remote as argument' '
-       echo "remote" >expected &&
-       __gitdir "remote" >"$actual" &&
-       test_cmp expected "$actual"
-'
-
-test_expect_success '__gitdir - .git directory in cwd' '
+test_expect_success '__git_find_repo_path - .git directory in cwd' '
        echo ".git" >expected &&
-       __gitdir >"$actual" &&
+       (
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - .git directory in parent' '
-       echo "$(pwd -P)/.git" >expected &&
+test_expect_success '__git_find_repo_path - .git directory in parent' '
+       echo "$ROOT/.git" >expected &&
        (
                cd subdir/subsubdir &&
-               __gitdir >"$actual"
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - cwd is a .git directory' '
+test_expect_success '__git_find_repo_path - cwd is a .git directory' '
        echo "." >expected &&
        (
                cd .git &&
-               __gitdir >"$actual"
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - parent is a .git directory' '
-       echo "$(pwd -P)/.git" >expected &&
+test_expect_success '__git_find_repo_path - parent is a .git directory' '
+       echo "$ROOT/.git" >expected &&
        (
                cd .git/refs/heads &&
-               __gitdir >"$actual"
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - $GIT_DIR set while .git directory in cwd' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+test_expect_success '__git_find_repo_path - $GIT_DIR set while .git directory in cwd' '
+       echo "$ROOT/otherrepo/.git" >expected &&
        (
-               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+               GIT_DIR="$ROOT/otherrepo/.git" &&
                export GIT_DIR &&
-               __gitdir >"$actual"
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - $GIT_DIR set while .git directory in parent' '
-       echo "$TRASH_DIRECTORY/otherrepo/.git" >expected &&
+test_expect_success '__git_find_repo_path - $GIT_DIR set while .git directory in parent' '
+       echo "$ROOT/otherrepo/.git" >expected &&
        (
-               GIT_DIR="$TRASH_DIRECTORY/otherrepo/.git" &&
+               GIT_DIR="$ROOT/otherrepo/.git" &&
                export GIT_DIR &&
                cd subdir &&
-               __gitdir >"$actual"
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_find_repo_path - from command line while "git -C"' '
+       echo "$ROOT/.git" >expected &&
+       (
+               __git_dir="$ROOT/.git" &&
+               __git_C_args=(-C otherrepo) &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - non-existing $GIT_DIR' '
+test_expect_success '__git_find_repo_path - relative dir from command line and "git -C"' '
+       echo "$ROOT/otherrepo/.git" >expected &&
        (
-               GIT_DIR="$TRASH_DIRECTORY/non-existing" &&
+               cd subdir &&
+               __git_dir="otherrepo/.git" &&
+               __git_C_args=(-C ..) &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_find_repo_path - $GIT_DIR set while "git -C"' '
+       echo "$ROOT/.git" >expected &&
+       (
+               GIT_DIR="$ROOT/.git" &&
                export GIT_DIR &&
-               test_must_fail __gitdir
-       )
+               __git_C_args=(-C otherrepo) &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
 '
 
-function pwd_P_W () {
-       if test_have_prereq MINGW
-       then
-               pwd -W
-       else
-               pwd -P
-       fi
-}
+test_expect_success '__git_find_repo_path - relative dir in $GIT_DIR and "git -C"' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               cd subdir &&
+               GIT_DIR="otherrepo/.git" &&
+               export GIT_DIR &&
+               __git_C_args=(-C ..) &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_find_repo_path - "git -C" while .git directory in cwd' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               __git_C_args=(-C otherrepo) &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
 
-test_expect_success '__gitdir - gitfile in cwd' '
-       echo "$(pwd_P_W)/otherrepo/.git" >expected &&
-       echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git &&
+test_expect_success '__git_find_repo_path - "git -C" while cwd is a .git directory' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               cd .git &&
+               __git_C_args=(-C .. -C otherrepo) &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_find_repo_path - "git -C" while .git directory in parent' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       (
+               cd subdir &&
+               __git_C_args=(-C .. -C otherrepo) &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_find_repo_path - non-existing path in "git -C"' '
+       (
+               __git_C_args=(-C non-existing) &&
+               test_must_fail __git_find_repo_path &&
+               printf "$__git_repo_path" >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_find_repo_path - non-existing path in $__git_dir' '
+       (
+               __git_dir="non-existing" &&
+               test_must_fail __git_find_repo_path &&
+               printf "$__git_repo_path" >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_find_repo_path - non-existing $GIT_DIR' '
+       (
+               GIT_DIR="$ROOT/non-existing" &&
+               export GIT_DIR &&
+               test_must_fail __git_find_repo_path &&
+               printf "$__git_repo_path" >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_find_repo_path - gitfile in cwd' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       echo "gitdir: $ROOT/otherrepo/.git" >subdir/.git &&
        test_when_finished "rm -f subdir/.git" &&
        (
                cd subdir &&
-               __gitdir >"$actual"
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - gitfile in parent' '
-       echo "$(pwd_P_W)/otherrepo/.git" >expected &&
-       echo "gitdir: $(pwd_P_W)/otherrepo/.git" >subdir/.git &&
+test_expect_success '__git_find_repo_path - gitfile in parent' '
+       echo "$ROOT/otherrepo/.git" >expected &&
+       echo "gitdir: $ROOT/otherrepo/.git" >subdir/.git &&
        test_when_finished "rm -f subdir/.git" &&
        (
                cd subdir/subsubdir &&
-               __gitdir >"$actual"
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success SYMLINKS '__gitdir - resulting path avoids symlinks' '
-       echo "$(pwd -P)/otherrepo/.git" >expected &&
+test_expect_success SYMLINKS '__git_find_repo_path - resulting path avoids symlinks' '
+       echo "$ROOT/otherrepo/.git" >expected &&
        mkdir otherrepo/dir &&
        test_when_finished "rm -rf otherrepo/dir" &&
        ln -s otherrepo/dir link &&
        test_when_finished "rm -f link" &&
        (
                cd link &&
+               __git_find_repo_path &&
+               echo "$__git_repo_path" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_find_repo_path - not a git repository' '
+       (
+               cd non-repo &&
+               GIT_CEILING_DIRECTORIES="$ROOT" &&
+               export GIT_CEILING_DIRECTORIES &&
+               test_must_fail __git_find_repo_path &&
+               printf "$__git_repo_path" >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__gitdir - finds repo' '
+       echo "$ROOT/.git" >expected &&
+       (
+               cd subdir/subsubdir &&
                __gitdir >"$actual"
        ) &&
        test_cmp expected "$actual"
 '
 
-test_expect_success '__gitdir - not a git repository' '
-       nongit test_must_fail __gitdir
+
+test_expect_success '__gitdir - returns error when cant find repo' '
+       (
+               __git_dir="non-existing" &&
+               test_must_fail __gitdir >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__gitdir - repo as argument' '
+       echo "otherrepo/.git" >expected &&
+       (
+               __gitdir "otherrepo" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__gitdir - remote as argument' '
+       echo "remote" >expected &&
+       (
+               __gitdir "remote" >"$actual"
+       ) &&
+       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' '
@@ -361,10 +517,657 @@ test_expect_success '__git_remotes - list remotes from $GIT_DIR/remotes and from
        git remote add remote_in_config_1 git://remote_1 &&
        test_when_finished "git remote remove remote_in_config_2" &&
        git remote add remote_in_config_2 git://remote_2 &&
-       __git_remotes >actual &&
+       (
+               __git_remotes >actual
+       ) &&
        test_cmp expect actual
 '
 
+test_expect_success '__git_is_configured_remote' '
+       test_when_finished "git remote remove remote_1" &&
+       git remote add remote_1 git://remote_1 &&
+       test_when_finished "git remote remove remote_2" &&
+       git remote add remote_2 git://remote_2 &&
+       (
+               verbose __git_is_configured_remote remote_2 &&
+               test_must_fail __git_is_configured_remote non-existent
+       )
+'
+
+test_expect_success 'setup for ref completion' '
+       git commit --allow-empty -m initial &&
+       git branch matching-branch &&
+       git tag matching-tag &&
+       (
+               cd otherrepo &&
+               git commit --allow-empty -m initial &&
+               git branch -m master master-in-other &&
+               git branch branch-in-other &&
+               git tag tag-in-other
+       ) &&
+       git remote add other "$ROOT/otherrepo/.git" &&
+       git fetch --no-tags other &&
+       rm -f .git/FETCH_HEAD &&
+       git init thirdrepo
+'
+
+test_expect_success '__git_refs - simple' '
+       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 - 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/ &&
+               __git_refs >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - repo given on the command line' '
+       cat >expected <<-EOF &&
+       HEAD
+       branch-in-other
+       master-in-other
+       tag-in-other
+       EOF
+       (
+               __git_dir="$ROOT/otherrepo/.git" &&
+               cur= &&
+               __git_refs >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - remote on local file system' '
+       cat >expected <<-EOF &&
+       HEAD
+       branch-in-other
+       master-in-other
+       tag-in-other
+       EOF
+       (
+               cur= &&
+               __git_refs otherrepo >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - remote on local file system - full refs' '
+       cat >expected <<-EOF &&
+       refs/heads/branch-in-other
+       refs/heads/master-in-other
+       refs/tags/tag-in-other
+       EOF
+       (
+               cur=refs/ &&
+               __git_refs otherrepo >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - configured remote' '
+       cat >expected <<-EOF &&
+       HEAD
+       branch-in-other
+       master-in-other
+       EOF
+       (
+               cur= &&
+               __git_refs other >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+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
+       EOF
+       (
+               cur=refs/ &&
+               __git_refs other >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - configured remote - repo given on the command line' '
+       cat >expected <<-EOF &&
+       HEAD
+       branch-in-other
+       master-in-other
+       EOF
+       (
+               cd thirdrepo &&
+               __git_dir="$ROOT/.git" &&
+               cur= &&
+               __git_refs other >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+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
+       EOF
+       (
+               cd thirdrepo &&
+               __git_dir="$ROOT/.git" &&
+               cur=refs/ &&
+               __git_refs other >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - configured remote - remote name matches a directory' '
+       cat >expected <<-EOF &&
+       HEAD
+       branch-in-other
+       master-in-other
+       EOF
+       mkdir other &&
+       test_when_finished "rm -rf other" &&
+       (
+               cur= &&
+               __git_refs other >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - URL remote' '
+       cat >expected <<-EOF &&
+       HEAD
+       branch-in-other
+       master-in-other
+       tag-in-other
+       EOF
+       (
+               cur= &&
+               __git_refs "file://$ROOT/otherrepo/.git" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+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
+       EOF
+       (
+               cur=refs/ &&
+               __git_refs "file://$ROOT/otherrepo/.git" >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success '__git_refs - non-existing remote' '
+       (
+               cur= &&
+               __git_refs non-existing >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_refs - non-existing remote - full refs' '
+       (
+               cur=refs/ &&
+               __git_refs non-existing >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_refs - non-existing URL remote' '
+       (
+               cur= &&
+               __git_refs "file://$ROOT/non-existing" >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_refs - non-existing URL remote - full refs' '
+       (
+               cur=refs/ &&
+               __git_refs "file://$ROOT/non-existing" >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_refs - not in a git repository' '
+       (
+               GIT_CEILING_DIRECTORIES="$ROOT" &&
+               export GIT_CEILING_DIRECTORIES &&
+               cd subdir &&
+               cur= &&
+               __git_refs >"$actual"
+       ) &&
+       test_must_be_empty "$actual"
+'
+
+test_expect_success '__git_refs - unique remote branches for git checkout DWIMery' '
+       cat >expected <<-EOF &&
+       HEAD
+       master
+       matching-branch
+       other/ambiguous
+       other/branch-in-other
+       other/master-in-other
+       remote/ambiguous
+       remote/branch-in-remote
+       matching-tag
+       branch-in-other
+       branch-in-remote
+       master-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= &&
+               __git_refs "" 1 >"$actual"
+       ) &&
+       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 &&
+       git remote remove other
+'
+
 test_expect_success '__git_get_config_variables' '
        cat >expect <<-EOF &&
        name-1
@@ -475,7 +1278,12 @@ test_expect_success 'general options plus command' '
        test_completion "git --namespace=foo check" "checkout " &&
        test_completion "git --paginate check" "checkout " &&
        test_completion "git --info-path check" "checkout " &&
-       test_completion "git --no-replace-objects check" "checkout "
+       test_completion "git --no-replace-objects check" "checkout " &&
+       test_completion "git --git-dir some/path check" "checkout " &&
+       test_completion "git -c conf.var=value check" "checkout " &&
+       test_completion "git -C some/path check" "checkout " &&
+       test_completion "git --work-tree some/path check" "checkout " &&
+       test_completion "git --namespace name/space check" "checkout "
 '
 
 test_expect_success 'git --help completion' '
@@ -483,10 +1291,10 @@ test_expect_success 'git --help completion' '
        test_completion "git --help core" "core-tutorial "
 '
 
-test_expect_success 'setup for ref completion' '
+test_expect_success 'setup for integration tests' '
        echo content >file1 &&
        echo more >file2 &&
-       git add . &&
+       git add file1 file2 &&
        git commit -m one &&
        git branch mybranch &&
        git tag mytag
@@ -500,6 +1308,12 @@ test_expect_success 'checkout completes ref names' '
        EOF
 '
 
+test_expect_success 'git -C <path> checkout uses the right repo' '
+       test_completion "git -C subdir -C subsubdir -C .. -C ../otherrepo checkout b" <<-\EOF
+       branch-in-other Z
+       EOF
+'
+
 test_expect_success 'show completes all refs' '
        test_completion "git show m" <<-\EOF
        master Z
@@ -517,7 +1331,7 @@ test_expect_success '<ref>: completes paths' '
 
 test_expect_success 'complete tree filename with spaces' '
        echo content >"name with spaces" &&
-       git add . &&
+       git add "name with spaces" &&
        git commit -m spaces &&
        test_completion "git show HEAD:nam" <<-\EOF
        name with spaces Z
@@ -526,7 +1340,7 @@ test_expect_success 'complete tree filename with spaces' '
 
 test_expect_success 'complete tree filename with metacharacters' '
        echo content >"name with \${meta}" &&
-       git add . &&
+       git add "name with \${meta}" &&
        git commit -m meta &&
        test_completion "git show HEAD:nam" <<-\EOF
        name with ${meta} Z
index 23c29bce6edbb5b921145e80849600896c2e1e3f..13b5696822d7cd054ed00cd5b4111ab39d3606cb 100644 (file)
@@ -625,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 b58f5c8cdc784bffd6de57c476f6e825411ee589..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,
@@ -303,7 +303,7 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v
                if (verbose)
                        fprintf(stderr, "updating local tracking ref '%s'\n", rs.dst);
                if (ref->deletion) {
-                       delete_ref(rs.dst, NULL, 0);
+                       delete_ref(NULL, rs.dst, NULL, 0);
                } else
                        update_ref("update by push", rs.dst,
                                        ref->new_oid.hash, NULL, 0, 0);
@@ -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;
@@ -471,11 +471,11 @@ void transport_print_push_status(const char *dest, struct ref *refs,
 {
        struct ref *ref;
        int n = 0;
-       unsigned char head_sha1[20];
+       struct object_id head_oid;
        char *head;
        int summary_width = transport_summary_width(refs);
 
-       head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_sha1, NULL);
+       head = resolve_refdup("HEAD", RESOLVE_REF_READING, head_oid.hash, NULL);
 
        if (verbose) {
                for (ref = refs; ref; ref = ref->next)
@@ -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))
@@ -1210,6 +1214,42 @@ char *transport_anonymize_url(const char *url)
        return xstrdup(url);
 }
 
+static void read_alternate_refs(const char *path,
+                               alternate_ref_fn *cb,
+                               void *data)
+{
+       struct child_process cmd = CHILD_PROCESS_INIT;
+       struct strbuf line = STRBUF_INIT;
+       FILE *fh;
+
+       cmd.git_cmd = 1;
+       argv_array_pushf(&cmd.args, "--git-dir=%s", path);
+       argv_array_push(&cmd.args, "for-each-ref");
+       argv_array_push(&cmd.args, "--format=%(objectname) %(refname)");
+       cmd.env = local_repo_env;
+       cmd.out = -1;
+
+       if (start_command(&cmd))
+               return;
+
+       fh = xfdopen(cmd.out, "r");
+       while (strbuf_getline_lf(&line, fh) != EOF) {
+               struct object_id oid;
+
+               if (get_oid_hex(line.buf, &oid) ||
+                   line.buf[GIT_SHA1_HEXSZ] != ' ') {
+                       warning("invalid line while parsing alternate refs: %s",
+                               line.buf);
+                       break;
+               }
+
+               cb(line.buf + GIT_SHA1_HEXSZ + 1, &oid, data);
+       }
+
+       fclose(fh);
+       finish_command(&cmd);
+}
+
 struct alternate_refs_data {
        alternate_ref_fn *fn;
        void *data;
@@ -1218,34 +1258,26 @@ struct alternate_refs_data {
 static int refs_from_alternate_cb(struct alternate_object_database *e,
                                  void *data)
 {
-       char *other;
-       size_t len;
-       struct remote *remote;
-       struct transport *transport;
-       const struct ref *extra;
+       struct strbuf path = STRBUF_INIT;
+       size_t base_len;
        struct alternate_refs_data *cb = data;
 
-       other = real_pathdup(e->path, 1);
-       len = strlen(other);
-
-       while (other[len-1] == '/')
-               other[--len] = '\0';
-       if (len < 8 || memcmp(other + len - 8, "/objects", 8))
+       if (!strbuf_realpath(&path, e->path, 0))
                goto out;
+       if (!strbuf_strip_suffix(&path, "/objects"))
+               goto out;
+       base_len = path.len;
+
        /* Is this a git repository with refs? */
-       memcpy(other + len - 8, "/refs", 6);
-       if (!is_directory(other))
+       strbuf_addstr(&path, "/refs");
+       if (!is_directory(path.buf))
                goto out;
-       other[len - 8] = '\0';
-       remote = remote_get(other);
-       transport = transport_get(remote, other);
-       for (extra = transport_get_remote_refs(transport);
-            extra;
-            extra = extra->next)
-               cb->fn(extra, cb->data);
-       transport_disconnect(transport);
+       strbuf_setlen(&path, base_len);
+
+       read_alternate_refs(path.buf, cb->fn, cb->data);
+
 out:
-       free(other);
+       strbuf_release(&path);
        return 0;
 }
 
index e597b31b38d9d7bb374af203b80e4b6e83abbc3d..bc5571574b67803249567bcee501dfcc342b0b0f 100644 (file)
@@ -255,6 +255,6 @@ int transport_refs_pushed(struct ref *ref);
 void transport_print_push_status(const char *dest, struct ref *refs,
                  int verbose, int porcelain, unsigned int *reject_reasons);
 
-typedef void alternate_ref_fn(const struct ref *, void *);
+typedef void alternate_ref_fn(const char *refname, const struct object_id *oid, void *);
 extern void for_each_alternate_ref(alternate_ref_fn, void *);
 #endif
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 132d342bc12bf790f9964f179385aa4f08e6ae00..4bbde924e8bf7e73f4f696d0319235ec56263a8f 100644 (file)
@@ -63,7 +63,50 @@ static int append_normalized_escapes(struct strbuf *buf,
        return 1;
 }
 
-char *url_normalize(const char *url, struct url_info *out_info)
+static const char *end_of_token(const char *s, int c, size_t n)
+{
+       const char *next = memchr(s, c, n);
+       if (!next)
+               next = s + n;
+       return next;
+}
+
+static int match_host(const struct url_info *url_info,
+                     const struct url_info *pattern_info)
+{
+       const char *url = url_info->url + url_info->host_off;
+       const char *pat = pattern_info->url + pattern_info->host_off;
+       int url_len = url_info->host_len;
+       int pat_len = pattern_info->host_len;
+
+       while (url_len && pat_len) {
+               const char *url_next = end_of_token(url, '.', url_len);
+               const char *pat_next = end_of_token(pat, '.', pat_len);
+
+               if (pat_next == pat + 1 && pat[0] == '*')
+                       /* wildcard matches anything */
+                       ;
+               else if ((pat_next - pat) == (url_next - url) &&
+                        !memcmp(url, pat, url_next - url))
+                       /* the components are the same */
+                       ;
+               else
+                       return 0; /* found an unmatch */
+
+               if (url_next < url + url_len)
+                       url_next++;
+               url_len -= url_next - url;
+               url = url_next;
+               if (pat_next < pat + pat_len)
+                       pat_next++;
+               pat_len -= pat_next - pat;
+               pat = pat_next;
+       }
+
+       return (!url_len && !pat_len);
+}
+
+static char *url_normalize_1(const char *url, struct url_info *out_info, char allow_globs)
 {
        /*
         * Normalize NUL-terminated url using the following rules:
@@ -104,7 +147,7 @@ char *url_normalize(const char *url, struct url_info *out_info)
        struct strbuf norm;
        size_t spanned;
        size_t scheme_len, user_off=0, user_len=0, passwd_off=0, passwd_len=0;
-       size_t host_off=0, host_len=0, port_len=0, path_off, path_len, result_len;
+       size_t host_off=0, host_len=0, port_off=0, port_len=0, path_off, path_len, result_len;
        const char *slash_ptr, *at_ptr, *colon_ptr, *path_start;
        char *result;
 
@@ -191,7 +234,12 @@ char *url_normalize(const char *url, struct url_info *out_info)
                strbuf_release(&norm);
                return NULL;
        }
-       spanned = strspn(url, URL_HOST_CHARS);
+
+       if (allow_globs)
+               spanned = strspn(url, URL_HOST_CHARS "*");
+       else
+               spanned = strspn(url, URL_HOST_CHARS);
+
        if (spanned < colon_ptr - url) {
                /* Host name has invalid characters */
                if (out_info) {
@@ -258,6 +306,7 @@ char *url_normalize(const char *url, struct url_info *out_info)
                                return NULL;
                        }
                        strbuf_addch(&norm, ':');
+                       port_off = norm.len;
                        strbuf_add(&norm, url, slash_ptr - url);
                        port_len = slash_ptr - url;
                }
@@ -265,7 +314,7 @@ char *url_normalize(const char *url, struct url_info *out_info)
                url = slash_ptr;
        }
        if (host_off)
-               host_len = norm.len - host_off;
+               host_len = norm.len - host_off - (port_len ? port_len + 1 : 0);
 
 
        /*
@@ -373,6 +422,7 @@ char *url_normalize(const char *url, struct url_info *out_info)
                out_info->passwd_len = passwd_len;
                out_info->host_off = host_off;
                out_info->host_len = host_len;
+               out_info->port_off = port_off;
                out_info->port_len = port_len;
                out_info->path_off = path_off;
                out_info->path_len = path_len;
@@ -380,6 +430,11 @@ char *url_normalize(const char *url, struct url_info *out_info)
        return result;
 }
 
+char *url_normalize(const char *url, struct url_info *out_info)
+{
+       return url_normalize_1(url, out_info, 0);
+}
+
 static size_t url_match_prefix(const char *url,
                               const char *url_prefix,
                               size_t url_prefix_len)
@@ -414,7 +469,7 @@ static size_t url_match_prefix(const char *url,
 
 static int match_urls(const struct url_info *url,
                      const struct url_info *url_prefix,
-                     int *exactusermatch)
+                     struct urlmatch_item *match)
 {
        /*
         * url_prefix matches url if the scheme, host and port of url_prefix
@@ -433,8 +488,8 @@ static int match_urls(const struct url_info *url,
         * contained a user name or false if url_prefix did not have a
         * user name.  If there is no match *exactusermatch is left untouched.
         */
-       int usermatched = 0;
-       int pathmatchlen;
+       char usermatched = 0;
+       size_t pathmatchlen;
 
        if (!url || !url_prefix || !url->url || !url_prefix->url)
                return 0;
@@ -454,33 +509,53 @@ static int match_urls(const struct url_info *url,
                usermatched = 1;
        }
 
-       /* check the host and port */
-       if (url_prefix->host_len != url->host_len ||
-           strncmp(url->url + url->host_off,
-                   url_prefix->url + url_prefix->host_off, url->host_len))
-               return 0; /* host names and/or ports do not match */
+       /* check the host */
+       if (!match_host(url, url_prefix))
+               return 0; /* host names do not match */
+
+       /* check the port */
+       if (url_prefix->port_len != url->port_len ||
+           strncmp(url->url + url->port_off,
+                   url_prefix->url + url_prefix->port_off, url->port_len))
+               return 0; /* ports do not match */
 
        /* check the path */
        pathmatchlen = url_match_prefix(
                url->url + url->path_off,
                url_prefix->url + url_prefix->path_off,
                url_prefix->url_len - url_prefix->path_off);
+       if (!pathmatchlen)
+               return 0; /* paths do not match */
 
-       if (pathmatchlen && exactusermatch)
-               *exactusermatch = usermatched;
-       return pathmatchlen;
+       if (match) {
+               match->hostmatch_len = url_prefix->host_len;
+               match->pathmatch_len = pathmatchlen;
+               match->user_matched = usermatched;
+       }
+
+       return 1;
+}
+
+static int cmp_matches(const struct urlmatch_item *a,
+                      const struct urlmatch_item *b)
+{
+       if (a->hostmatch_len != b->hostmatch_len)
+               return a->hostmatch_len < b->hostmatch_len ? -1 : 1;
+       if (a->pathmatch_len != b->pathmatch_len)
+               return a->pathmatch_len < b->pathmatch_len ? -1 : 1;
+       if (a->user_matched != b->user_matched)
+               return b->user_matched ? -1 : 1;
+       return 0;
 }
 
 int urlmatch_config_entry(const char *var, const char *value, void *cb)
 {
        struct string_list_item *item;
        struct urlmatch_config *collect = cb;
-       struct urlmatch_item *matched;
+       struct urlmatch_item matched = {0};
        struct url_info *url = &collect->url;
        const char *key, *dot;
        struct strbuf synthkey = STRBUF_INIT;
-       size_t matched_len = 0;
-       int user_matched = 0;
        int retval;
 
        if (!skip_prefix(var, collect->section, &key) || *(key++) != '.') {
@@ -494,13 +569,13 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
                struct url_info norm_info;
 
                config_url = xmemdupz(key, dot - key);
-               norm_url = url_normalize(config_url, &norm_info);
+               norm_url = url_normalize_1(config_url, &norm_info, 1);
                free(config_url);
                if (!norm_url)
                        return 0;
-               matched_len = match_urls(url, &norm_info, &user_matched);
+               retval = match_urls(url, &norm_info, &matched);
                free(norm_url);
-               if (!matched_len)
+               if (!retval)
                        return 0;
                key = dot + 1;
        }
@@ -510,24 +585,18 @@ int urlmatch_config_entry(const char *var, const char *value, void *cb)
 
        item = string_list_insert(&collect->vars, key);
        if (!item->util) {
-               matched = xcalloc(1, sizeof(*matched));
-               item->util = matched;
+               item->util = xcalloc(1, sizeof(matched));
        } else {
-               matched = item->util;
-               /*
-                * Is our match shorter?  Is our match the same
-                * length, and without user while the current
-                * candidate is with user?  Then we cannot use it.
-                */
-               if (matched_len < matched->matched_len ||
-                   ((matched_len == matched->matched_len) &&
-                    (!user_matched && matched->user_matched)))
+               if (cmp_matches(&matched, item->util) < 0)
+                        /*
+                         * Our match is worse than the old one,
+                         * we cannot use it.
+                         */
                        return 0;
                /* Otherwise, replace it with this one. */
        }
 
-       matched->matched_len = matched_len;
-       matched->user_matched = user_matched;
+       memcpy(item->util, &matched, sizeof(matched));
        strbuf_addstr(&synthkey, collect->section);
        strbuf_addch(&synthkey, '.');
        strbuf_addstr(&synthkey, key);
index 528862adc55c43ed26763b3c05e1d27d558a1b74..37ee5da85e2dd3f0ba3ee12d0c515fa6def2a043 100644 (file)
@@ -18,11 +18,12 @@ struct url_info {
        size_t passwd_len;      /* length of passwd; if passwd_off != 0 but
                                   passwd_len == 0, an empty passwd was given */
        size_t host_off;        /* offset into url to start of host name (0 => none) */
-       size_t host_len;        /* length of host name; this INCLUDES any ':portnum';
+       size_t host_len;        /* length of host name;
                                 * file urls may have host_len == 0 */
-       size_t port_len;        /* if a portnum is present (port_len != 0), it has
-                                * this length (excluding the leading ':') at the
-                                * end of the host name (always 0 for file urls) */
+       size_t port_off;        /* offset into url to start of port number (0 => none) */
+       size_t port_len;        /* if a portnum is present (port_off != 0), it has
+                                * this length (excluding the leading ':') starting
+                                * from port_off (always 0 for file urls) */
        size_t path_off;        /* offset into url to the start of the url path;
                                 * this will always point to a '/' character
                                 * after the url has been normalized */
@@ -33,7 +34,8 @@ struct url_info {
 extern char *url_normalize(const char *, struct url_info *);
 
 struct urlmatch_item {
-       size_t matched_len;
+       size_t hostmatch_len;
+       size_t pathmatch_len;
        char user_matched;
 };
 
index 2125d6da26dbdc01f7397415ef1f67616e6f2a71..8b732e40bce4968257c2918219f7dfdb0b9ed3a0 100644 (file)
@@ -262,25 +262,22 @@ struct userdiff_driver *userdiff_find_by_name(const char *name) {
 
 struct userdiff_driver *userdiff_find_by_path(const char *path)
 {
-       static struct git_attr *attr;
-       struct git_attr_check check;
-
-       if (!attr)
-               attr = git_attr("diff");
-       check.attr = attr;
+       static struct attr_check *check;
 
+       if (!check)
+               check = attr_check_initl("diff", NULL);
        if (!path)
                return NULL;
-       if (git_check_attr(path, 1, &check))
+       if (git_check_attr(path, check))
                return NULL;
 
-       if (ATTR_TRUE(check.value))
+       if (ATTR_TRUE(check->items[0].value))
                return &driver_true;
-       if (ATTR_FALSE(check.value))
+       if (ATTR_FALSE(check->items[0].value))
                return &driver_false;
-       if (ATTR_UNSET(check.value))
+       if (ATTR_UNSET(check->items[0].value))
                return NULL;
-       return userdiff_find_by_name(check.value);
+       return userdiff_find_by_name(check->items[0].value);
 }
 
 struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)
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;
+}
diff --git a/ws.c b/ws.c
index ea4b2b1dfd64ce41f4752a93f4d5478c4e53deda..a07caedd5a565bbf29e4f9b3036ac60e6e9f716e 100644 (file)
--- a/ws.c
+++ b/ws.c
@@ -71,24 +71,17 @@ unsigned parse_whitespace_rule(const char *string)
        return rule;
 }
 
-static void setup_whitespace_attr_check(struct git_attr_check *check)
-{
-       static struct git_attr *attr_whitespace;
-
-       if (!attr_whitespace)
-               attr_whitespace = git_attr("whitespace");
-       check[0].attr = attr_whitespace;
-}
-
 unsigned whitespace_rule(const char *pathname)
 {
-       struct git_attr_check attr_whitespace_rule;
+       static struct attr_check *attr_whitespace_rule;
+
+       if (!attr_whitespace_rule)
+               attr_whitespace_rule = attr_check_initl("whitespace", NULL);
 
-       setup_whitespace_attr_check(&attr_whitespace_rule);
-       if (!git_check_attr(pathname, 1, &attr_whitespace_rule)) {
+       if (!git_check_attr(pathname, attr_whitespace_rule)) {
                const char *value;
 
-               value = attr_whitespace_rule.value;
+               value = attr_whitespace_rule->items[0].value;
                if (ATTR_TRUE(value)) {
                        /* true (whitespace) */
                        unsigned all_rule = ws_tab_width(whitespace_rule_cfg);
index 4bb46781c8a7e580d4a8f6ae0dd48f8478b2d01d..03754849626d1bf117e3821fb7cdffcd594f694f 100644 (file)
@@ -121,7 +121,7 @@ static void status_printf_more(struct wt_status *s, const char *color,
 
 void wt_status_prepare(struct wt_status *s)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        memset(s, 0, sizeof(*s));
        memcpy(s->color_palette, default_wt_status_colors,
@@ -129,7 +129,7 @@ void wt_status_prepare(struct wt_status *s)
        s->show_untracked_files = SHOW_NORMAL_UNTRACKED_FILES;
        s->use_color = -1;
        s->relative_paths = 1;
-       s->branch = resolve_refdup("HEAD", 0, sha1, NULL);
+       s->branch = resolve_refdup("HEAD", 0, oid.hash, NULL);
        s->reference = "HEAD";
        s->fp = stdout;
        s->index_file = get_index_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:
@@ -1115,16 +1128,16 @@ static void abbrev_sha1_in_line(struct strbuf *line)
 
        split = strbuf_split_max(line, ' ', 3);
        if (split[0] && split[1]) {
-               unsigned char sha1[20];
+               struct object_id oid;
 
                /*
                 * strbuf_split_max left a space. Trim it and re-add
                 * it after abbreviation.
                 */
                strbuf_trim(split[1]);
-               if (!get_sha1(split[1]->buf, sha1)) {
+               if (!get_oid(split[1]->buf, &oid)) {
                        strbuf_reset(split[1]);
-                       strbuf_add_unique_abbrev(split[1], sha1,
+                       strbuf_add_unique_abbrev(split[1], oid.hash,
                                                 DEFAULT_ABBREV);
                        strbuf_addch(split[1], ' ');
                        strbuf_reset(line);
@@ -1340,7 +1353,7 @@ static void show_bisect_in_progress(struct wt_status *s,
 static char *get_branch(const struct worktree *wt, const char *path)
 {
        struct strbuf sb = STRBUF_INIT;
-       unsigned char sha1[20];
+       struct object_id oid;
        const char *branch_name;
 
        if (strbuf_read_file(&sb, worktree_git_path(wt, "%s", path), 0) <= 0)
@@ -1354,9 +1367,9 @@ static char *get_branch(const struct worktree *wt, const char *path)
                strbuf_remove(&sb, 0, branch_name - sb.buf);
        else if (starts_with(sb.buf, "refs/"))
                ;
-       else if (!get_sha1_hex(sb.buf, sha1)) {
+       else if (!get_oid_hex(sb.buf, &oid)) {
                strbuf_reset(&sb);
-               strbuf_add_unique_abbrev(&sb, sha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&sb, oid.hash, DEFAULT_ABBREV);
        } else if (!strcmp(sb.buf, "detached HEAD")) /* rebase */
                goto got_nothing;
        else                    /* bisect */
@@ -1370,10 +1383,10 @@ static char *get_branch(const struct worktree *wt, const char *path)
 
 struct grab_1st_switch_cbdata {
        struct strbuf buf;
-       unsigned char nsha1[20];
+       struct object_id noid;
 };
 
-static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
+static int grab_1st_switch(struct object_id *ooid, struct object_id *noid,
                           const char *email, unsigned long timestamp, int tz,
                           const char *message, void *cb_data)
 {
@@ -1387,13 +1400,13 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
                return 0;
        target += strlen(" to ");
        strbuf_reset(&cb->buf);
-       hashcpy(cb->nsha1, nsha1);
+       oidcpy(&cb->noid, noid);
        end = strchrnul(target, '\n');
        strbuf_add(&cb->buf, target, end - target);
        if (!strcmp(cb->buf.buf, "HEAD")) {
                /* HEAD is relative. Resolve it to the right reflog entry. */
                strbuf_reset(&cb->buf);
-               strbuf_add_unique_abbrev(&cb->buf, nsha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&cb->buf, noid->hash, DEFAULT_ABBREV);
        }
        return 1;
 }
@@ -1402,7 +1415,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
 {
        struct grab_1st_switch_cbdata cb;
        struct commit *commit;
-       unsigned char sha1[20];
+       struct object_id oid;
        char *ref = NULL;
 
        strbuf_init(&cb.buf, 0);
@@ -1411,22 +1424,22 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
                return;
        }
 
-       if (dwim_ref(cb.buf.buf, cb.buf.len, sha1, &ref) == 1 &&
+       if (dwim_ref(cb.buf.buf, cb.buf.len, oid.hash, &ref) == 1 &&
            /* sha1 is a commit? match without further lookup */
-           (!hashcmp(cb.nsha1, sha1) ||
+           (!oidcmp(&cb.noid, &oid) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
-            ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
-             !hashcmp(cb.nsha1, commit->object.oid.hash)))) {
+            ((commit = lookup_commit_reference_gently(oid.hash, 1)) != NULL &&
+             !oidcmp(&cb.noid, &commit->object.oid)))) {
                const char *from = ref;
                if (!skip_prefix(from, "refs/tags/", &from))
                        skip_prefix(from, "refs/remotes/", &from);
                state->detached_from = xstrdup(from);
        } else
                state->detached_from =
-                       xstrdup(find_unique_abbrev(cb.nsha1, DEFAULT_ABBREV));
-       hashcpy(state->detached_sha1, cb.nsha1);
-       state->detached_at = !get_sha1("HEAD", sha1) &&
-                            !hashcmp(sha1, state->detached_sha1);
+                       xstrdup(find_unique_abbrev(cb.noid.hash, DEFAULT_ABBREV));
+       hashcpy(state->detached_sha1, cb.noid.hash);
+       state->detached_at = !get_oid("HEAD", &oid) &&
+                            !hashcmp(oid.hash, state->detached_sha1);
 
        free(ref);
        strbuf_release(&cb.buf);
@@ -1476,22 +1489,22 @@ void wt_status_get_state(struct wt_status_state *state,
                         int get_detached_from)
 {
        struct stat st;
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (!stat(git_path_merge_head(), &st)) {
                state->merge_in_progress = 1;
        } else if (wt_status_check_rebase(NULL, state)) {
                ;               /* all set */
        } else if (!stat(git_path_cherry_pick_head(), &st) &&
-                       !get_sha1("CHERRY_PICK_HEAD", sha1)) {
+                       !get_oid("CHERRY_PICK_HEAD", &oid)) {
                state->cherry_pick_in_progress = 1;
-               hashcpy(state->cherry_pick_head_sha1, sha1);
+               hashcpy(state->cherry_pick_head_sha1, oid.hash);
        }
        wt_status_check_bisect(NULL, state);
        if (!stat(git_path_revert_head(), &st) &&
-           !get_sha1("REVERT_HEAD", sha1)) {
+           !get_oid("REVERT_HEAD", &oid)) {
                state->revert_in_progress = 1;
-               hashcpy(state->revert_head_sha1, sha1);
+               hashcpy(state->revert_head_sha1, oid.hash);
        }
 
        if (get_detached_from)
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;
index 7389ce41022dbee5776d44a937e8bcf780656f42..8c88dbde3827ce9d1b5e4f95287513514b80fac1 100644 (file)
@@ -183,16 +183,14 @@ int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb,
 
                                /*
                                 * We don't need additional context if
-                                * a whole function was added, possibly
-                                * starting with empty lines.
+                                * a whole function was added.
                                 */
-                               while (i2 < xe->xdf2.nrec &&
-                                      is_empty_rec(&xe->xdf2, i2))
+                               while (i2 < xe->xdf2.nrec) {
+                                       if (match_func_rec(&xe->xdf2, xecfg, i2,
+                                               dummy, sizeof(dummy)) >= 0)
+                                               goto post_context_calculation;
                                        i2++;
-                               if (i2 < xe->xdf2.nrec &&
-                                   match_func_rec(&xe->xdf2, xecfg, i2,
-                                                  dummy, sizeof(dummy)) >= 0)
-                                       goto post_context_calculation;
+                               }
 
                                /*
                                 * Otherwise get more context from the